像这样转义 SQL 查询安全吗?
Is escaping SQL queries like this safe?
我目前正在开发一个 NodeJS 后端脚本,该脚本解析传入的 HTTP 请求以写入和读取 MySQL 数据库以进行工作。我试图通过使用一种双层保护来保护它免受 SQL 注入。
要写入数据库,用户需要提供 有效 JSON。这就是 JSON 的键 需要 以便脚本写入数据库的方式。否则,请求将被取消。
{
"uuid": "1234-5678-abcd-efgh",
"product_id": 6,
"product_extras": "color=red",
"product_count": 2,
"buyer_name": "X Y",
"shipping_address": "XY Street 5"
}
检查此输入的第一层是一个非常基本的黑名单。这里的USER_INPUT
变量就是上面已经过验证的JSON
let BLACKLIST = ["DROP ", "DELETE ", "INSERT ", "UPDATE ", "SELECT ", "WHERE ", "ALTER "];
let dont_execute = false;
for(var i = 0; i < BLACKLIST.length; i++) {
if(JSON.stringify(USER_INPUT).toUpperCase().includes(BLACKLIST[i].toUpperCase())) {
console.log("\x1b[31mreceived blacklisted command - aborting");
dont_execute = true;
return false;
}
}
验证后,如果 dont_execute
仍然为假,将调用第二层,因此查询将被转义并像这样发送:
// setting.sqlconnection.table_name is equal to "orders" in this case. Also this variable can't be changed or specified by the user. It's pulled from a settings.json file
sql.sendQuery("INSERT INTO " + setting.sqlconnection.table_name + " (uuid, productid, orderid, productextras, productcount, buyername, shippingaddress) VALUES (" + sqlconnection.escape(uuid) + ", " + sqlconnection.escape(parseddata.product_id) + ", " + null + ", " + sqlconnection.escape(parseddata.product_extras) + ", " + sqlconnection.escape(parseddata.product_count) + ", " + sqlconnection.escape(parseddata.buyer_name) + ", " + sqlconnection.escape(parseddata.shipping_address) + ")");
我试过很多不同的注射方法,比如这个:
{
"uuid": "1234-5678-abcd-efgh",
"product_id": 6,
"product_extras": "color=red",
"product_count": 2,
"buyer_name": "X Y",
"shipping_address": "');DROP orders;--"
}
正如我所料,它没有起作用,因为首先它在第一个黑名单层中被咳嗽。但是在禁用它之后只是为了测试整个 SQL 查询只是被解释为一个字符串,因为 apostrophe 被转义了('
变成了 \'
),这是正是我想要的。据我所知,将与 SQLI-infected JSON 一起发送的查询最终将如下所示:
INSERT INTO orders (guid, productid, orderid, productextras, productcount, buyername, shippingaddress) VALUES ("1234-5678-abcd-efgh", 6, null, "color=red", 2, "X Y", "\');DROP orders--")
但问题是,在向我的老板展示了这个之后,他说它不安全并且它仍然容易受到 SQLI 的攻击。所以我的问题是他是否正确,如果正确,我可以做些什么来改进它。
附加信息:
我正在使用 npm 包 mysql
进行数据库连接
我正在使用 XAMPP 和 MySQL 在本地托管数据库
很抱歉,如果我不能提供更多信息,但我很确定我 不允许 到 post 比这更多的东西。
拒绝名单方法必然会遗漏某些情况。您需要更多地研究查询是如何形成的,并且您应该为您的代码编写全面的单元测试,以便审查您的代码的任何人都可以看到您测试了哪些案例。
拒绝名单方法也会产生误报。例如,您似乎无法插入任何包含单词“DROP”的数据。这将阻止一些合法的数据值。
这两个问题都可以通过使用参数化查询来解决,正如上面的评论所建议的那样。您说您将“看一看”使用参数,但您应该将其视为防止 SQL 注入的 主要 解决方案,而不是任何类型的可选或高级用法。
但是,参数仅可用于代替标量值(字符串、数字、日期)。您不能为 table 名称或列名或其他标识符、SQL 表达式或 SQL 关键字使用参数。这些情况可能不太常见,但您的动态 table 名称中至少有一个示例。
有害的输入不仅仅来自用户。它可以来自文件、Web 服务、JSON 文档。它甚至可以来自您自己的数据库!任何可能包含奇怪字符的内容都可能导致 SQL 注入。
SQL 注入不一定是恶意的。这可能只是一个错误,更有可能导致您的 SQL 查询无效,而不是导致数据泄露。
补充参数化查询的解决方案通常是白名单。例如,如果您想知道配置文件中的 table 名称是否合法,请对照已知的 table 名称列表进行检查。一些应用程序将其保存在一个常量数组中。某些应用程序查询 INFORMATION_SCHEMA 以获取 table 的最新列表。
曾经在名为 ORDER
的电子商务数据库中看到过 table 吗?这会导致 SQL 变得混乱,因为 ORDER
是一个保留字。
您应该在反引号中分隔 table 名称,以防它们是 SQL 保留字或包含标点符号或空格。
这是在 table 名称周围添加反引号的示例:
sql.sendQuery("INSERT INTO `" + setting.sqlconnection.table_name + "` (uuid, ...
我目前正在开发一个 NodeJS 后端脚本,该脚本解析传入的 HTTP 请求以写入和读取 MySQL 数据库以进行工作。我试图通过使用一种双层保护来保护它免受 SQL 注入。
要写入数据库,用户需要提供 有效 JSON。这就是 JSON 的键 需要 以便脚本写入数据库的方式。否则,请求将被取消。
{
"uuid": "1234-5678-abcd-efgh",
"product_id": 6,
"product_extras": "color=red",
"product_count": 2,
"buyer_name": "X Y",
"shipping_address": "XY Street 5"
}
检查此输入的第一层是一个非常基本的黑名单。这里的USER_INPUT
变量就是上面已经过验证的JSON
let BLACKLIST = ["DROP ", "DELETE ", "INSERT ", "UPDATE ", "SELECT ", "WHERE ", "ALTER "];
let dont_execute = false;
for(var i = 0; i < BLACKLIST.length; i++) {
if(JSON.stringify(USER_INPUT).toUpperCase().includes(BLACKLIST[i].toUpperCase())) {
console.log("\x1b[31mreceived blacklisted command - aborting");
dont_execute = true;
return false;
}
}
验证后,如果 dont_execute
仍然为假,将调用第二层,因此查询将被转义并像这样发送:
// setting.sqlconnection.table_name is equal to "orders" in this case. Also this variable can't be changed or specified by the user. It's pulled from a settings.json file
sql.sendQuery("INSERT INTO " + setting.sqlconnection.table_name + " (uuid, productid, orderid, productextras, productcount, buyername, shippingaddress) VALUES (" + sqlconnection.escape(uuid) + ", " + sqlconnection.escape(parseddata.product_id) + ", " + null + ", " + sqlconnection.escape(parseddata.product_extras) + ", " + sqlconnection.escape(parseddata.product_count) + ", " + sqlconnection.escape(parseddata.buyer_name) + ", " + sqlconnection.escape(parseddata.shipping_address) + ")");
我试过很多不同的注射方法,比如这个:
{
"uuid": "1234-5678-abcd-efgh",
"product_id": 6,
"product_extras": "color=red",
"product_count": 2,
"buyer_name": "X Y",
"shipping_address": "');DROP orders;--"
}
正如我所料,它没有起作用,因为首先它在第一个黑名单层中被咳嗽。但是在禁用它之后只是为了测试整个 SQL 查询只是被解释为一个字符串,因为 apostrophe 被转义了('
变成了 \'
),这是正是我想要的。据我所知,将与 SQLI-infected JSON 一起发送的查询最终将如下所示:
INSERT INTO orders (guid, productid, orderid, productextras, productcount, buyername, shippingaddress) VALUES ("1234-5678-abcd-efgh", 6, null, "color=red", 2, "X Y", "\');DROP orders--")
但问题是,在向我的老板展示了这个之后,他说它不安全并且它仍然容易受到 SQLI 的攻击。所以我的问题是他是否正确,如果正确,我可以做些什么来改进它。
附加信息:
我正在使用 npm 包 mysql
进行数据库连接
我正在使用 XAMPP 和 MySQL 在本地托管数据库
很抱歉,如果我不能提供更多信息,但我很确定我 不允许 到 post 比这更多的东西。
拒绝名单方法必然会遗漏某些情况。您需要更多地研究查询是如何形成的,并且您应该为您的代码编写全面的单元测试,以便审查您的代码的任何人都可以看到您测试了哪些案例。
拒绝名单方法也会产生误报。例如,您似乎无法插入任何包含单词“DROP”的数据。这将阻止一些合法的数据值。
这两个问题都可以通过使用参数化查询来解决,正如上面的评论所建议的那样。您说您将“看一看”使用参数,但您应该将其视为防止 SQL 注入的 主要 解决方案,而不是任何类型的可选或高级用法。
但是,参数仅可用于代替标量值(字符串、数字、日期)。您不能为 table 名称或列名或其他标识符、SQL 表达式或 SQL 关键字使用参数。这些情况可能不太常见,但您的动态 table 名称中至少有一个示例。
有害的输入不仅仅来自用户。它可以来自文件、Web 服务、JSON 文档。它甚至可以来自您自己的数据库!任何可能包含奇怪字符的内容都可能导致 SQL 注入。
SQL 注入不一定是恶意的。这可能只是一个错误,更有可能导致您的 SQL 查询无效,而不是导致数据泄露。
补充参数化查询的解决方案通常是白名单。例如,如果您想知道配置文件中的 table 名称是否合法,请对照已知的 table 名称列表进行检查。一些应用程序将其保存在一个常量数组中。某些应用程序查询 INFORMATION_SCHEMA 以获取 table 的最新列表。
曾经在名为 ORDER
的电子商务数据库中看到过 table 吗?这会导致 SQL 变得混乱,因为 ORDER
是一个保留字。
您应该在反引号中分隔 table 名称,以防它们是 SQL 保留字或包含标点符号或空格。
这是在 table 名称周围添加反引号的示例:
sql.sendQuery("INSERT INTO `" + setting.sqlconnection.table_name + "` (uuid, ...