如何在 PHP 应用程序中不使用 PDO 或绑定的情况下解决二阶 SQL 注入问题

How to address second order SQL injection without using PDO or binding in PHP application

提问人:user332951 提问时间:9/12/2020 更新时间:9/14/2020 访问量:248

问:

如何在 PHP 应用程序中不使用 PDO 或 mysqli 绑定的情况下解决二阶 SQL 注入问题。我做了很多研究,但都指向我没有的PDO。mysql_real_escape_string PHP 转义只是为了防止引号。 现在我们有使用mysql_connect的旧 PHP。我们无法升级系统以使用 PDO 或 MySQLi,因为它需要安装依赖项。为了克服注射,我们使用逃逸。但这并不能防止二阶 SQL 注入。

$name = $_POST['名称'];$user =“从名称 = $name 的用户中选择用户”

现在使用 $id 获取用户位置。请注意,当前代码不执行 join,因为这只是示例。

$loc = “从用户 = $user的位置选择 *”

目前,我们正在使用Escape进行$name和$user来防止注射。但问题是,如果第一个查询包含SQL查询,那么我们就有问题了。因此,我们需要一种方法来转义查询,这样第二个查询将是安全的

php mysql pdo sql 注入

评论

3赞 Marcin Orlowski 9/12/2020
mysql_real_escape_string PHP escape is just for protection against the quotes.谁告诉你这些废话?也被弃用了很长时间,所以你似乎在那里有非常过时的设置mysql_
2赞 Your Common Sense 9/12/2020
一阶或 N 阶 SQL 注入之间没有区别。保护都是一样的
0赞 Dharman 9/12/2020
应该对要插入 SQL 的每个字符串进行转义。不管是一阶还是二阶。只有一个 SQL 注入。但是,使用 PHP 5 是非常不负责任的。如果你认真对待你的用户,你应该尽一切努力尽快升级。
1赞 Bill Karwin 9/14/2020
上面的评论是错误的,他们显然没有阅读您的代码示例。他们很懒惰,只是自动关闭几乎所有标记为 PHP/SQL 注入的问题,作为 stackoverflow.com/questions/60174/ 的副本......这个很棒,它回答了大多数SQL注入问题,但它并不涵盖您的情况。

答:

0赞 Bill Karwin 9/14/2020 #1

您说得对,在将整个子查询插入到位置查询中时不能使用。mysql_real_escape_string()

$name = $_POST['name'];

$user = "Select user from user where name = $name"

$user_esc = mysql_real_escape_string($user); -- WRONG

$loc = "Select * from location where user = $user"

防御 SQL 注入的建议通常归结为“使用查询参数”,这在许多情况下是正确的,但在这种情况下它不起作用。

escape-string 函数和绑定查询参数都仅用于保护 SQL 表达式中的单个值。对于动态 SQL 查询的其他部分,这两种方法都不是有用的:

  • 标识符,如表名或列名
  • 值列表(例如,在谓词中)IN(...)
  • SQL 关键字
  • SQL 表达式
  • 整个 SQL 子查询,就像您的情况一样。

在您的示例中,子查询是应用控制下的固定字符串,但 $name 变量除外。如果转义该变量,则它不会受到 SQL 注入的影响。

$name = $_POST['name'];
$name_esc = mysql_real_escape_string($name);

$user_query = "Select user from user where name = '$name_esc'"

然后,您可以将该子查询用作第二个查询的一部分,并且不存在 SQL 注入的风险。

$loc_query = "Select * from location where user IN ($user_query)"

顺便说一句,我做了两个小改动:

  • 将子查询放在括号内
  • 使用代替 .如果子查询可以在其结果中返回多行,则不允许使用 .IN( )==

我还建议您学习如何在 SQL 中使用连接。这是使用 SQL 的一种普通且推荐的方法。通常,如果正确索引了表,则使用联接的查询比使用子查询的查询具有更好的性能,即使两个查询产生相同的结果也是如此。下面是一个示例:

$name = $_POST['name'];
$name_esc = mysql_real_escape_string($name);

$loc_query = "Select loc.* from user join loc using (user) 
    where user.name = '$name_esc'";

我可能会因为给你一个适用于 mysql_real_escape_string() 的解决方案而被否决,因为该函数已被弃用,并且已从当前版本的 PHP 中删除。

我建议升级到当前版本的 PHP,并且我建议使用 PDO 和查询参数。它们更简单、更安全,并且具有更好的性能。

$loc_query = "Select loc.* from user join loc using (user) 
    where user.name = ?";

$stmt = $pdo->prepare($loc_query);
$stmt->execute( [ $_POST['name'] ] );

使用查询参数时,无需对 post 变量进行转义。

您还会发现 PHP 7+ 通常比 PHP 5.x 具有更好的性能。

你不能升级是不正确的。这需要一些工作。

评论

0赞 Your Common Sense 9/14/2020
你的陈述“转义字符串函数......仅用于保护 SQL 表达式中的单个值“是不正确的,并且具有误导性。不是“值”,而是字符串文字
0赞 Bill Karwin 9/14/2020
或引用日期文字。或带引号的数字文字(这很奇怪,但允许)。我相信您知道我同意使用查询参数是一个更好的解决方案。但是 OP 可能处于他们的老板没有预算来支付升级工作的情况。
0赞 Your Common Sense 9/14/2020
mysql中没有“日期文字”这样的东西。也没有“带引号的数字文字”。它要么是数字文本,要么是字符串文本。字符串是字节或字符的序列,包含在单引号 (') 或双引号 (“) 字符中,无论里面的特定字符是什么。