【问题标题】:Parameterized Dynamic Queries / SQL Sanitation NodeJS参数化动态查询/SQL Sanitation NodeJS
【发布时间】:2019-12-14 09:36:38
【问题描述】:

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

以下代码的问题是过滤器是可选的,具体取决于用户传递给函数的内容,因此它们的顺序可以改变(很难单独传递每个过滤器参数)。据我所知,Sqlstring 使用顺序来确定哪些参数与正确的问号匹配。

所以我只剩下一次将所有过滤器传递给 Sqlstring,但如果没有过滤器处于活动状态,那么我只剩下一个用于过滤器变量的空字符串,这将引发 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;

有没有更好的方法来做到这一点?

【问题讨论】:

  • SQL 表/结果集按照 SQL 标准定义是 orderless,因此使用 LIMIT 而不使用 ORDER BY a.id(假设这里的 id 有一个 PRIMARY KEY)来获得第一个匹配100 条记录几乎毫无意义。

标签: javascript mysql node.js sql-injection


【解决方案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_1 到 tbl_foo_99。并且您希望您的用户能够访问所有这些。但是如果后来有人添加了tbl_foo_secret,会发生什么?你猜怎么了?您的用户也可以访问它,因为您让他们选择自己的表名。或者如果你添加一个完整的模式,tbl_foo_top_secret?他们可以很容易地告诉您表后缀是tbl_foo_top_secret.table_name,并且您的代码会很好地接受它,因为在这个库中句号不会转义。因此,您需要添加自己的自定义检查以确保表名后缀是可接受的。

【讨论】:

  • 感谢您的回复!代码示例并不清楚,但表名附加了客户 ID,该 ID 在身份验证时直接从数据库中提取。有没有一种全局处理这个问题的好方法(比如在查询函数中)?我不想让编写函数的人单独转义所有用户参数。
  • 不是真的;您必须分别转义每个参数,否则您并不能真正防止注入攻击。我经常做的事情(几年前在我开始使用准备好的语句之前)是在例程的顶部立即转义所有变量,这样很容易看到,你不必搜索代码为了它。很高兴听到来自数据库的表后缀,而不是用户输入。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-09-10
  • 1970-01-01
  • 2012-06-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多