哪些页面易受 SQL 注入攻击?

信息安全 sql注入 脆弱性
2021-09-06 13:45:57

我相信我了解 SQL 注入的基础知识。我也知道对 PHP 文件使用准备好的语句是防止 SQL 注入的最佳方法。我总是被告知,当攻击者在面向公众的站点上的表单数据字段或文件输入字段中输入有效的 SQL 命令时,SQL 注入最常发生。

但是,如果我的网站上有 PHP 文件只能由经过身份验证的用户访问,是否仍然 100% 需要使用准备好的语句?

此外,不需要任何外部用户数据即可运行的 SQL 查询呢?

就像是:

SELECT * FROM tableName

如果我没有将任何变量传递给查询,它是否仍然容易受到 SQL 注入的攻击?

4个回答

您是否完全信任所有经过身份验证的用户?包括他们的帐户不会被攻击者入侵?如果攻击者可以访问一个帐户,那就不好了,但如果他们可以利用它来窃取您的数据库的其余部分,那就更糟了。

对于第二点,您使用的示例不会容易受到攻击。但是,请注意更多边界情况,例如

$query = "SELECT * FROM tableName WHERE secret='".dbquery("SELECT secret FROM otherTable WHERE id=3")."'";

otherTable如果可以在3secret的记录列中插入有效负载,则即使插入例程使用准备好的语句id,这也容易受到Second Order SQLi的攻击。

根据评论:

SELECT * FROM tableName WHERE secret=(SELECT secret FROM otherTable WHERE id=3)

在 SQL 中执行,所以不是问题。当从数据库中提取的数据在应用层被认为是可信的,并在以后的查询中使用时,就会出现问题。

你说“我相信我了解 SQL 注入的基础知识”。但是问题的其余部分暗示您可能仍然有些困惑。无论哪种方式,这是一个重要的话题,许多人仍然对问题和解决方案感到困惑。

我也知道对 PHP 文件使用准备好的语句是防止 SQL 注入的最佳方法。

使用准备好的语句是防止 SQL 注入 (SQLi) 的必要但非充分条件。要了解原因,了解 SQL 注入漏洞的原因很重要。以下是原因:

  1. 连接来自用户/客户端输入的 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不会有任何结果。但是让我们明确一点,您可以将第一种(错误的)方法与准备好的语句一起使用,您仍然会很脆弱。准备好的语句中没有什么神奇的东西可以清理输入。这是您不能将第二种(正确)方法与不支持参数的常规语句一起使用。

我认为通常理解这一点的人会使用速记“使用准备好的语句”,因为使用它们的主要原因是因为它们支持参数。但真正解决这个问题的是参数。

除了 Matthew 的出色回答之外,即使您信任您的用户,您仍然可能容易受到CSRF的攻击,这可能允许攻击者在没有帐户的情况下执行 SQL 注入。

攻击者可能能够创建一个链接或表单,经过身份验证的用户可能会无意中单击或提交该链接或表单,从而导致 SQL 注入发生。出于这个原因,我断言你永远不应该相信用户输入,你应该对所有用户输入进行清理和使用参数化查询。

这是 Stack Overflow 上一个非常受欢迎的问题,这让我对它产生了一种“标准反应”。

1. 你为什么要讨价还价?

首先,这个问题听起来像是您试图在开发过程中为自己讨价还价。但你为什么要这么努力?为什么会出现这样的问题?准备好的语句对你来说太难掌握/实施了吗?然后你应该问一个不同的问题,“我如何让准备好的陈述不那么乏味?” 事实上,准备好的语句比任何其他数据库交互方法都更容易。

因此,首先,根本没有理由提出这样的问题。

2. SQL注入只是一个副作用

无论任何可能的威胁如何,您都需要格式化查询。你这样做,不是为了著名的Bobby Tables,而是为了一个谦逊的女孩 Sarah O'Hara (或任何其他碰巧拥有可能使数据库窒息的名字的人或实体,如果格式不正确) .

因此,这不是您必须使用准备好的语句的 SQL 注入,而只是为了保证您的查询在语法上总是正确的。

3. 手动格式化有太多可能的陷阱

不使用准备好的语句意味着您将不得不手动格式化数据,并且这条道路上有很多陷阱。我试图在我的一篇关于 SQL 注入的文章中总结它们。这是一段摘录:

  1. 手动格式化可能不完整。 让我们以 Bobby Tables 为例。这是不完整格式的完美示例:我们添加到查询中的字符串仅被引用,但没有被转义!然而,正如我们刚刚从上面学到的那样,引用和转义应该始终一起进行(以及为转义函数设置正确的编码)。但是在一个单独进行 SQL 字符串格式化的普通 PHP 应用程序中(部分在查询中,部分在其他地方),很可能会简单地忽略格式化的某些部分。

  2. 手动格式化可以应用于错误的文字只要我们使用完整的格式化,这没什么大不了的(因为它会立即导致错误,可以在开发阶段修复),但是当与不完整的格式化结合使用时,这是一场真正的灾难。Stack Overflow 的大网站上有数百个答案,建议以与字符串相同的方式转义标识符。这将是完全无用的,并且会导致 SQL 注入。

  3. 手动格式化本质上是一种非强制性措施首先,有一个明显缺乏注意的情况,在这种情况下,可以简单地忘记正确的格式。但是有一个非常奇怪的情况——许多 PHP 用户经常故意拒绝应用任何格式,因为直到今天他们仍然将数据分为“干净”和“不干净”,“用户输入”和“非用户输入”,等认为“安全”数据不需要任何格式。这简直是​​胡说八道——记住莎拉·奥哈拉。从格式化的角度来看,重要的是目的地开发人员必须注意SQL 文字的类型,而不是数据源。它是一个要查询的字符串吗?然后必须将其格式化为字符串。无论是来自用户输入还是只是在代码执行过程中神秘地出现。

  4. 手动格式化可以与实际查询执行相距相当远的距离。

最被低估和忽视的问题。然而,其中最重要的是,如果不遵守,它可能会破坏所有其他规则。

几乎每个 PHP 用户都想在一个远离实际查询执行的地方完成所有“清理”工作,而这种错误的方法本身就是无数错误的根源:

  • 首先,由于手头没有查询,人们无法分辨出这一段代表什么样的 SQL 文字——因此我们同时违反了格式规则 (1) 和 (2)。
  • 有不止一个地方进行消毒(它可以是集中式设施或就地格式化),我们正在呼吁一场灾难,因为一个开发人员会认为它是由另一个开发人员完成的,或者已经在其他地方制作,等等。
  • 有多个地方进行清理,我们引入了另一种危险,即双重清理数据(例如,一个开发人员在入口点格式化它,另一个 - 在查询执行之前),这并不危险,但会使一个网站看起来非常不专业
  • 过早格式化会破坏源变量,使其无法用于其他任何东西。

    1. 毕竟,手动格式化总是会在代码中占用额外的空间,使其纠缠不清、臃肿不堪。

4. 准备好的语句不是灵丹妙药。

最后但并非最不重要的。我们都必须记住,数据文字不是查询中唯一的可变部分。有时还必须添加标识符或关键字。在这种情况下,准备好的语句没有帮助,但是这样的查询部分应该有同样的保护。幸运的是,对于标识符和关键字,可能的变体列表总是有限的,所以我们可以使用我在对 Stack Overflow 上的参考问题的回答中描述的白名单方法,所以我不会重复自己。


要回答您的最后一个问题 - 不,没有可变部分的查询不容易受到攻击,因为没有必要注入。