你说“我相信我了解 SQL 注入的基础知识”。但是问题的其余部分暗示您可能仍然有些困惑。无论哪种方式,这是一个重要的话题,许多人仍然对问题和解决方案感到困惑。
我也知道对 PHP 文件使用准备好的语句是防止 SQL 注入的最佳方法。
使用准备好的语句是防止 SQL 注入 (SQLi) 的必要但非充分条件。要了解原因,了解 SQL 注入漏洞的原因很重要。以下是原因:
- 连接来自用户/客户端输入的 SQL 语句。
而已。如果您从用户那里获取输入并将其放入 SQL 语句中,然后在数据库上进行编译,您可能(并且很可能)受到 SQLi 漏洞的影响。如果您这样做并使用准备好的语句,那么您什么也解决不了。你仍然很脆弱。
那么为什么总是使用准备好的语句作为答案呢?如果我们不能从输入中连接 SQL,这意味着我们的 SQL 需要或多或少的硬编码。但是,如果您想撤回刚刚登录的用户信息怎么办?我们不能每次有新用户时都编写新的 SQL 语句。
准备好的报表
使准备好的语句在消除此问题方面有用的原因是它们允许使用参数。这意味着不要做这样的事情:
// Don't do this! Subject to injection
"SELECT credit_card_number FROM user_data where userid = '" + userinput + "'"
我们做得到:
"SELECT credit_card_number FROM user_data where userid = ?"
并在数据库上编译这个语句。当我们去运行它时,我们需要传入一个参数值,否则查询将失败。为什么这能解决问题?假设攻击者弄清楚如何获取字符串:foo' OR 1 = 1 OR 'a' = 'a
输入到用户输入值。在第一种情况下,将执行以下 SQL:
SELECT credit_card_number FROM user_data where userid = 'foo' OR 1 = 1 OR 'a' = 'a';
返回表中的每一行。在第二种情况下,此 SQL 将执行:
SELECT credit_card_number FROM user_data where userid = ?
与参数foo' OR 1 = 1 OR 'a' = 'a
。除非您的用户的 id 不太可能,否则foo' OR 1 = 1 OR 'a' = 'a
不会有任何结果。但是让我们明确一点,您可以将第一种(错误的)方法与准备好的语句一起使用,您仍然会很脆弱。准备好的语句中没有什么神奇的东西可以清理输入。这是您不能将第二种(正确)方法与不支持参数的常规语句一起使用。
我认为通常理解这一点的人会使用速记“使用准备好的语句”,因为使用它们的主要原因是因为它们支持参数。但真正解决这个问题的是参数。