参数化动态查询/ SQL卫生NodeJS

时间:2019-08-06 19:21:47

标签: javascript mysql node.js sql-injection

我对节点还很陌生,并且使用sqlstring进行动态参数化查询时遇到了问题。

以下代码的问题在于,过滤器是可选的,具体取决于用户向函数传递的内容,因此过滤器的顺序可以更改(这使得很难分别传递每个过滤器参数)。据我所知Sqlstring使用顺序来确定哪些参数匹配正确的问号。

因此,我一次将所有过滤器都传递给Sqlstring,但是如果没有任何过滤器处于活动状态,那么我只会留下一个空字符串作为filter变量,这将引发sql语法错误。

let filter = idList !== '' ? ` AND id IN(${idList})` : '';
filter += locationsList !== '' ? ` AND a.locationID IN(${locationsList})` : '';
filter += start !== undefined ? ` AND a.lastEdited >= ${start}` : '';
filter += end !== undefined ? ` AND a.lastEdited <= ${end}` : '';
filter += name !== undefined ? ` AND a.name LIKE '%${name}%'` : '';

const qry = `
SELECT
a.id 'id'
,a.number 'number'
,a.name 'name'
,a.locationID 'locationID'
,a.location 'location'
,a.lastEdited 'lastEdited'
,a.userID 'owner'
FROM tbl_foo_${'?'} a
WHERE a.id > ${'?'} ${'?'} LIMIT ${'?'};`;

let values = [ id, cursor, filter, limit ];

const rows = query(db, qry, values);

//inside of the query function it does this and then runs the query against the database
if (qry.includes('?')) {
sanitizedQry = sqlstring.format(qry, values);
}

它产生的查询如下:

SELECT
a.id 'id'
,a.number 'number'
,a.name 'name'
,a.locationID 'locationID'
,a.location 'location'
,a.lastEdited 'lastEdited'
,a.userID 'owner'
FROM tbl_foo_36 a
WHERE a.id > 1 '' LIMIT 100;

有更好的方法吗?

1 个答案:

答案 0 :(得分:1)

如果您看看sqlstring npm page,它说的是关于SqlString.format的内容:

  

这看起来类似于MySQL中的预备语句,但是实际上它内部仅使用相同的SqlString.escape()方法。

通过适当使用SqlString.escape,您会变得更好。

但是问题是,您不能防止在需要的地方进行SQL注入。例如,在此语句中,您应该具有注入保护,而您没有。这是它的外观示例:

filter += name !== undefined ? ` AND a.name LIKE '%${SqlString.escape(name)}%'` : '';

name大概是用户输入变量,此时您需要防止SQL注入。所有用户输入变量都需要直接应用应用SQL注入保护。

在已经使用变量构造了SQL片段之后,您绝对不能提供SQL注入保护。构建filter SQL片段,然后尝试稍后再插入时,您正在做什么……实际上是您在进行有意的SQL注入。

免责声明::使用准备好的语句(或等效技术)将获得比此转义样式保护更高的保护级别。转义样式保护是针对SQL注入攻击的最低保护级别,您可以使用它,但仍然有任何保护。

免责声明2:允许用户输入表名的一部分也非常危险,并且很难正确逃脱。

例如,假设您有一堆表,tbl_foo_1tbl_foo_99。您希望用户可以访问所有这些内容。但是,如果以后有人添加tbl_foo_secret会怎样?你猜怎么了?您的用户也可以访问它,因为您可以让他们选择自己的表名。或者,如果您添加整个架构tbl_foo_top_secret,该怎么办?他们可以很容易地告诉您表后缀为tbl_foo_top_secret.table_name,而您的代码也可以接受它,因为在该库中,不会转义。因此,您需要添加自己的自定义检查,以确保表名后缀是可接受的。