仅使用PHP防止SQL注入攻击

6
问题是我有一个完整的工作网站,其中涉及对MySQL服务器的多次调用。在研究这个网站时,我发现以以下形式进行查询可以提高查询效率:
$query = sprintf("SELECT * FROM users WHERE user='%s' AND password='%s'",
            mysql_real_escape_string($user),
            mysql_real_escape_string($password));

我可以解决安全问题,但是,就像我说的那样,我有很多访问MySQL服务器的调用,而在我的情况下,解决问题的最佳方法是直接访问我传递给查询的变量,但不使用MySQL函数,因为我已经超出了查询范畴。让我解释一下,我有这个:

mysql_query("SELECT * FROM `post` WHERE id=" . $_GET['edit']);

我不能修改这个查询,因为我在我的所有代码中都有很多这样的查询。相反,我更喜欢检查变量$_GET['edit']上的注入。

如何使用纯PHP检查查询中变量的SQL注入?例如:

$_GET['edit']=freehack($_GET['edit']);

1
看到有多少人点赞和收藏了这个,我简直不敢相信周围有这么多糟糕的开发者!所以所有这些人都有同样的问题,并寻找着同样类型的解决方案? - Your Common Sense
另外,据我所知,最好不要转义你的密码。如果你要用它进行比较,请加密密码。 - Nick
3
我无法修改此查询,因为我在我的代码中有很多这样的查询。面对一个糟糕的缺陷,懒惰是不美好的表现。 - Andrew
6个回答

12

不要这样做。通过用“安全”的版本替换您的$_GET参数值,您正在污染您的输入数据,而您可能需要在其他地方使用它。

只有在需要在数据库层使用数据时才转义数据。花点时间修复查询将为您节省大量麻烦。

无论如何,你现在做的还是不安全的!参见:PHP: Is mysql_real_escape_string sufficient for cleaning user input?

你真的应该使用PDO准备查询。此外,在使用查询之前,您应该检查用户输入的有效性。


总之:在转义字符串时,请使用mysql_real_escape_string()。在转义整数时,请使用intval()。 - TehShrike
“在查询中使用用户输入之前,您应该检查其有效性”是人们有时会忘记的重要部分。如果您期望一个整数,为什么不检查它是否为整数,而不是尝试将任何内容适配到查询中呢? - HoLyVieR
@TehShrike 在转义标识符时,请使用白名单。 - Your Common Sense
@Col. Shrapnel你的意思是在构建查询之前,应该选择该标识符的所有合法值,并确保您的值与其中一个匹配? - TehShrike
@TehShrike 是的。对于预处理语句使用,该语句仍然保持不变,这使得你的前两个陈旧无用。 - Your Common Sense
дҪҝз”Ёintval()иҖҢдёҚжҳҜ(int)жңүд»Җд№ҲеҺҹеӣ еҗ—пјҹ - Andrew

7
“我不能对这个查询进行修改,因为我在所有代码中都有很多这样的查询”,这种态度在谈到安全性时是错误的。你所拥有的是一个重大的设计问题,它会给你带来各种安全问题。即使你像最后描述的过滤方法那样做了一些事情,你也无法确定你将涵盖每种情况。
你应该真正使用某种数据库访问类来查询数据库,而不是在代码中随机调用。这样一来,你可以编写一次消毒代码,并确信它被所有地方调用。为了增加您的安全性,重构是值得的。

1

我认为你可以将你的查询放在PDO中。

$unsafe = "SELECT * FROM `post` WHERE id=" . $_GET['edit'];
$DBH->quote($unsafe); // makes query safe.

在这里,$DBH = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);

然后你需要编写一些脚本来进行替换。但我真的认为你应该从头开始重写你的代码,因为它真的很丑陋。要进行适当的单元测试、PDO、CSRF保护、面向对象编程等。


0

我不会使用“神奇”函数并期望它清理变量。这不会解决所有边缘情况,仍然会有漏洞。

一个很好的例子是你的第二个查询。即使你使用mysql_real_escape_string函数来处理$_GET['edit']变量,它仍然容易受到SQL注入攻击的影响

你需要做的是验证你接收到的所有数据,并检查它是否是你所期望的数据类型。

if (SomeValidator::validateInt($_GET['edit'])) {
    // OK, we continue //
} else {
    // Display error, but don't continue ! //
}

过滤只是为了确保数据能够正确显示且不会引起问题。您不应该仅依赖过滤来验证数据。

如果您想正确地进行验证和过滤,可以使用ESAPI for PHP


0

我建议使用 PDO接口或 MySQLi接口,因为两者都支持使用预处理查询。使用预处理查询是有效且一致地保护自己免受SQL注入攻击的唯一方法。我个人推荐PDO而不是mysqli,因为它提供了一个与数据库无关的接口,以防您需要切换数据库。即使您永远不需要切换数据库,只学习一个接口也比以后需要在不同的项目中使用不同数据库时需要学习多个接口更好。


0
  1. 避免在应用程序的不同位置重复使用相同的查询 - 随着事情的发展,您需要维护多个版本,并且它们几乎总是不同步的。对我来说,准备好的查询听起来不错(我不是真正的PHP专家),或者如果您必须采取另一种方法,请设置一个中央参考库并将查询存储在其中,然后在应用程序中需要时引用它们。

  2. 不要存储编码数据,因为很容易忘记哪些变量已编码,哪些没有,而且一旦您必须将数据用于具有不同编码的目的,您就会陷入麻烦。尽可能晚地进行编码处理,最好是在放入需要编码的最终情况时进行编码处理,以便快速检查显示正在进行编码处理。

  3. 如果必须... SQL注入显著是一种类型安全问题。如果您知道您期望的是整数参数,则“1; drop table users;--”不是有效输入,即使它没有任何危险字符或转义序列。不仅要检查字符串是否被破坏,还要确保当您需要特定类型时,您得到该类型,其他输入会引发错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接