在调用存储过程时如何避免从Node.js进行SQL注入
假设从UI的前端输入了一些特殊字符
例如:
如果输入?
,true
将保存到数据库
如果输入??
,`true`
将保存到数据库
用于一些特殊字符,例如反斜杠(\
)和撇号('
)
我将从控制台获得这些类型的错误
From console: '
{ Error: ER_PARSE_ERROR: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near
'''')' at line 1
at Query.Sequence._packetToError (C:\xampp\htdocs\nodechat\node_modules\mysql\lib\protocol\sequences\Sequence.js:47:14)
at Query.ErrorPacket (C:\xampp\htdocs\nodechat\node_modules\mysql\lib\protocol\sequences\Query.js:77:18)
at Protocol._parsePacket (C:\xampp\htdocs\nodechat\node_modules\mysql\lib\protocol\Protocol.js:278:23)
at Parser.write (C:\xampp\htdocs\nodechat\node_modules\mysql\lib\protocol\Parser.js:76:12)
at Protocol.write (C:\xampp\htdocs\nodechat\node_modules\mysql\lib\protocol\Protocol.js:38:16)
at Socket.<anonymous> (C:\xampp\htdocs\nodechat\node_modules\mysql\lib\Connection.js:91:28)
at Socket.<anonymous> (C:\xampp\htdocs\nodechat\node_modules\mysql\lib\Connection.js:502:10)
at Socket.emit (events.js:182:13)
at addChunk (_stream_readable.js:283:12)
at readableAddChunk (_stream_readable.js:264:11)
--------------------
at Protocol._enqueue (C:\xampp\htdocs\nodechat\node_modules\mysql\lib\protocol\Protocol.js:144:48)
at Connection.query (C:\xampp\htdocs\nodechat\node_modules\mysql\lib\Connection.js:200:25)
at Object.saveFeeds (C:\xampp\htdocs\nodechat\middleware\db.js:96:15)
at C:\xampp\htdocs\nodechat\middleware\routes.js:187:12
at Layer.handle [as handle_request] (C:\xampp\htdocs\nodechat\node_modules\express\lib\router\layer.js:95:5)
at next (C:\xampp\htdocs\nodechat\node_modules\express\lib\router\route.js:137:13)
at Route.dispatch (C:\xampp\htdocs\nodechat\node_modules\express\lib\router\route.js:112:3)
at Layer.handle [as handle_request] (C:\xampp\htdocs\nodechat\node_modules\express\lib\router\layer.js:95:5)
at C:\xampp\htdocs\nodechat\node_modules\express\lib\router\index.js:281:22
at Function.process_params (C:\xampp\htdocs\nodechat\node_modules\express\lib\router\index.js:335:12)
code: 'ER_PARSE_ERROR',
errno: 1064,
sqlMessage:
'You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near \'\'\'\')\' at line
1',
sqlState: '42000',
index: 0,
sql: 'CALL AddFeedItems(1,\'\'\')' }
undefined
C:\xampp\htdocs\nodechat\node_modules\mysql\lib\protocol\Parser.js:80
throw err; // Rethrow non-MySQL errors
^
TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be one of type string or Buffer. Received type undefined
at write_ (_http_outgoing.js:595:11)
at ServerResponse.write (_http_outgoing.js:567:10)
at C:\xampp\htdocs\nodechat\middleware\routes.js:188:17
at Query.<anonymous> (C:\xampp\htdocs\nodechat\middleware\db.js:100:13)
at Query.<anonymous> (C:\xampp\htdocs\nodechat\node_modules\mysql\lib\Connection.js:502:10)
at Query._callback (C:\xampp\htdocs\nodechat\node_modules\mysql\lib\Connection.js:468:16)
at Query.Sequence.end (C:\xampp\htdocs\nodechat\node_modules\mysql\lib\protocol\sequences\Sequence.js:83:24)
at Query.ErrorPacket (C:\xampp\htdocs\nodechat\node_modules\mysql\lib\protocol\sequences\Query.js:90:8)
at Protocol._parsePacket (C:\xampp\htdocs\nodechat\node_modules\mysql\lib\protocol\Protocol.js:278:23)
at Parser.write (C:\xampp\htdocs\nodechat\node_modules\mysql\lib\protocol\Parser.js:76:12)
routes.js
app.post('/AddFeedItems', function(req, res) {
// console.log(req.body);
try{
console.log(JSON.parse(Object.keys(req.body)[0]));
req.body = JSON.parse(Object.keys(req.body)[0]);
} catch(err) {
console.log('Error');
req.body = req.body
}
db.saveFeeds(req.body, function(chats) {
res.write(JSON.stringify(chats));
res.end();
})
});
db.js
function saveFeeds(data,cb) {
const conn = createConnection();
conn.connect();
console.log('From console'+data.keyword);
let sql ="CALL AddFeedItems("+data.senderid + ",'" + data.keyword + "')";
conn.query(sql, true,function(err,result) {
if(err) console.log(err);
conn.end();
console.log(result);
cb(result);
});
}
MySQL存储过程
CREATE PROCEDURE `AddFeedItems`(IN `senderid` BIGINT(255), IN `keyword` VARCHAR(255)) NOT DETERMINISTIC CONTAINS SQL SQL SECURITY DEFINER
BEGIN
DECLARE LastFeedId INT;
INSERT INTO `feed_item` (`userid`, `content`, `timestamp`, `likes`, `comments`, `user_flag`, `likes_data`) VALUES (senderid, keyword, CURRENT_TIMESTAMP(), 0, 0, 0, 'like');
SET LastFeedId = LAST_INSERT_ID();
INSERT INTO `feed_item_likes` (`feed_item_id`, `user_id`, `timestamp`, `is_like`) VALUES (LastFeedId, senderid, CURRENT_TIMESTAMP(), 0);
SELECT LastFeedId;
END
通常,为了防止将这些特殊字符注入到SQL中。规范中应采取哪些必要步骤和预防措施?
需要一个函数来剥离一组特殊字符,例如反斜杠( \
),美元( $
),撇号(<来自Node.js的strong> '
)和问号( ?
)
因此,我找到了替换上述所有特殊字符(问号( ?
)除外)的解决方案。
以下是获得的结果:
正则表达式中带有问号
data.keyword.replace(/[\\$'"\?]/g, "\\$&")
+-----------+-----------+
|Entered |Saved into |
|Character |Database |
+-----------+-----------+
| ? | rue |
| ?? | rue? |
| ??? | ??? |
+-----------+-----------+
正则表达式中没有问号
data.keyword.replace(/[\\$'"]/g, "\\$&")
+-----------+-----------+
|Entered |Saved into |
|Character |Database |
+-----------+-----------+
| ? | true |
| ?? | `true` |
| ??? | ??? |
+-----------+-----------+
尝试使用内置功能
var key = mysql.escape(data.keyword);
var key = conn.escape(data.keyword);
let sql ="CALL AddFeedFriendItems("+data.senderid + "," + data.friendid + ",'" + data.friendusername + "'," + key + ")";
结果仍然不令人满意,问号(?
)将存储为true
。
我需要仅用 ?
值替换那些问号( ?
)。取而代之的是,将 true
或 rue
值存储到数据库中。
我应该如何编写正则表达式以匹配问号并仅用相同的字符替换?
答案 0 :(得分:0)
问题不在您的存储过程中,而在您的CALL语句中:
sql: 'CALL AddFeedItems(1,\'\'\')' }
这将导致一条SQL语句:
CALL AddFeedItems(1,''')
连续三个'
引号无效的语法。它在CALL语句上引发语法错误,它永远不会超出运行存储过程的范围。
如果要用引号引起来的字符串包含文字单引号,则SQL必须为以下形式之一:
CALL AddFeedItems(1,'''') -- two single-quotes become one literal single-quote
CALL AddFeedItems(1,'\'') -- escaped single-quote
CALL AddFeedItems(1,"'") -- delimit by alternative quotes, if sql_mode is not ANSI
答案 1 :(得分:0)
或者,我用Regex + Unicode +内置函数找到了解决方案
var key = conn.escape(data.keyword);
var keyword = key.replace(/[?]/g, "❓");
let sql ="CALL AddFeedFriendItems("+data.senderid + "," + data.friendid + ",'" + data.friendusername + "'," + keyword + ")";
List of Unicode Characters with Question Mark
在MySQL中,默认排序规则将设置为latin1_swedish_ci
。这会混淆是否保存哪种类型的字符。很自然,它将以纯文本格式保存?
。尽管通常所有Unicode字符?
都将保存在数据库中。由于归类设置为latin1_swedish_ci
注意:需要一些有关将Unicode字符保存到MySQL中的研究,这会影响性能或B-Tree索引检索记录会引发任何其他问题。由于.replace()
将在存储多于999K消息时进一步降低性能。因为
默认情况下,在MySQL中,排序规则将设置为latin1_swedish_ci