这段时间维护Node.js编写的项目,发现登陆SQL注入问题。经过一番折腾解决了这个问题,现记录如下:
一、问题描述
const crypto = require('crypto');
const conn = require('../../libs/Conn');
const mysql = require('mysql');
module.exports = async function (params) {
let hash = crypto.createHash('sha256');
let login_name = params.login_name;
let password = hash.update(params.password).digest('hex');
let sql = `SELECT uf.* FROM user_file uf WHERE uf.login_name = '${login_name}' AND uf.password = '${password}' AND
uf.delete_sign = 1;
`;
let user = (await conn(sql)).shift();
return user;
};
这样的方式很容易被人在动态参数中加入特殊字符产生sql注入,威胁数据库的安全。
如果 login_name 输入'or'1'='1,SQL语句会变成 uf.login_name = '' or '1' = '1' AND uf.password = ' '
也就是说不需要知道登录名,只要输密码就可以。
二、解决方案
如果对参数使用escape,就能将参数中的特殊字符进行转义,防止sql的注入。
WHERE uf.login_name = `+ mysql.escape(login_name) +` AND uf.password = `+ mysql.escape(password) +`
三、拓展
3.1 使用escape()对传入参数进行编码
mysql.escape(param)
connection.escape(param)
pool.escape(param)
3.2 escape()方法编码规则如下
:
Numbers不进行转换;
Booleans转换为true/false;
Date对象转换为’YYYY-mm-dd HH:ii:ss’字符串;
Buffers转换为hex字符串,如X’0fa5’;
Strings进行安全转义;
Arrays转换为列表,如[‘a’, ‘b’]会转换为’a’, ‘b’;
多维数组转换为组列表,如[[‘a’, ‘b’], [‘c’, ‘d’]]会转换为’a’, ‘b’), (‘c’, ‘d’);
Objects会转换为key=value键值对的形式。嵌套的对象转换为字符串;
undefined/null会转换为NULL;
MySQL不支持NaN/Infinity,并且会触发MySQL错误。