【问题标题】:Is hexing input sufficient to sanitize SQL Queries?十六进制输入是否足以清理 SQL 查询?
【发布时间】:2014-04-29 08:42:02
【问题描述】:

我昨晚在阅读有关防止 SQL 注入的内容,我遇到了这个答案:

How can I prevent SQL injection in PHP?

“你的常识”中的 cmets 听起来像是功能失调/不安全。然而,在我的(尽管有限的)测试中,我发现 php 的“bin2hex($var)”适用于我扔给它的任何东西——文字数字、数字字符串、文本字符串——即使在匹配数字 (tinyint) 列时也是如此。

我的问题是:当每个用户输入都通过十六进制进行清理时,有没有办法注入 SQL?从本质上讲,任何时候进行查询时,它都会看起来像这样:

$query="SELECT * FROM table WHERE someidentifier=UNHEX('".bin2hex($unsafe_user_input)."') LIMIT 1"

基本上翻译成:

SELECT * FROM table WHERE someidentifier=UNHEX('0b99f') LIMIT 1

这种类型的安全性是否存在漏洞?

PS - 我不只是在寻找诸如“为什么不将 PDO 或 MySQLi 与准备好的语句一起使用?”之类的答案?它可能属于抢先优化的巨大弊端,但我宁愿不要将我的查询开销加倍(是的,我明白使用多个相同的查询可以更快,但这不是我经常遇到的情况)。

【问题讨论】:

  • 不要推出自己的消毒功能。这很可能是安全的,但是当您可以使用标准的“保证安全”版本时,为什么还要发明自己的“可能安全”系统?
  • 在 PDO 和/或 MySQLi(最好是后者)中是否有不涉及双倍给定查询开销的清理方法?我在 w3schools 上阅读了一些关于过滤器的信息,但我知道它们不是一个非常值得信赖的来源......
  • 您可以通过手动转义来滚动您自己的查询,例如real_escape_string。准备查询的开销是真实的,但在宏伟的计划中它是非常小的。只是不要犯在插入循环内准备查询的错误。准备工作应该做一个。然后,您只需多次执行该准备好的语句。
  • 请记住那些说“不要编写自己的查询,使用 pdo/mysqli+prepared 语句”来防止 sql 注入攻击的人似乎从未意识到您仍然可以编写完全可注入的查询不无论您使用的是什么数据库库。他们是工具。如果你锯断了你的腿,那不是电锯的错。 PDO/mysqli 可以帮助编写安全查询,但不能让您编写安全查询。
  • 非常好的注意事项,感谢您指出这些。您能想出一种可以将其注入的方法吗?

标签: php mysql sql sql-injection sanitize


【解决方案1】:

当每个用户输入都通过十六进制进行清理时,有没有办法注入 SQL?

如果您知道为什么会发生 SQL 注入,您就可以自己回答这个问题。


让我们看看。 CWE describes SQL injections (CWE-89)如下:

该软件使用受外部影响的输入构建全部或部分 SQL 命令 [...],但它不会中和或错误地中和可能修改预期 SQL 命令的特殊元素 [...]

此外:

如果在用户可控输入中没有充分删除或引用 SQL 语法,生成的 SQL 查询可能会导致这些输入被解释为 SQL 而不是普通用户数据。

所以基本上:生成的 SQL 查询中受外部影响的输入不会按预期解释。这里的重要部分是:未按预期解释

如果用户输入的意图是解释为MySQL string literal,但事实并非如此,则它是 SQL 注入。但是为什么会这样呢?

好吧,string literals 具有特定的语法,SQL 解析器通过这些语法来识别它们:

字符串是由单引号 (“'”) 或双引号 (“"”) 字符括起来的字节或字符序列。

另外:

在字符串中,某些序列具有特殊含义 […]。这些序列中的每一个都以反斜杠(“\”)开头,称为转义字符。 MySQL 可以识别Table 9.1, “Special Character Escape Sequences” 中显示的转义序列。

此外,为了能够在字符串文字中使用引号:

有几种方法可以在字符串中包含引号字符:

  • 用“'”引用的字符串中的“'”可以写成“''”。
  • 用“"”引用的字符串中的“"”可以写成“""”。
  • 在引号字符前加上转义字符 (“\”)。
  • 用“"”引用的字符串中的“'”不需要特殊处理,也不需要加倍或转义。同样,用“'”引用的字符串中的“"”不需要特殊处理。

由于后面提到的所有这些序列对于字符串文字来说都是特殊的,因此任何旨在被解释为字符串文字的数据都必须经过适当的处理以符合这些规则。这尤其意味着:如果任何提到的字符打算在字符串文字中使用,它们必须以上述方式之一编写。

因此,如果您从这个角度来看,这甚至不是安全问题,而只是处理数据以使它们按预期进行解释

这同样适用于其他文字以及 SQL 的其他方面。


那你的问题呢?

我的问题是:当每个用户输入都通过十六进制进行清理时,有没有办法注入 SQL?从本质上讲,任何时候进行查询时,它都会看起来像这样:

$query="SELECT * FROM table WHERE someidentifier=UNHEX('".bin2hex($unsafe_user_input)."') LIMIT 1"

是的,这对 SQL 注入是安全的。 bin2hex 返回一个只包含十六进制字符的字符串。在 MySQL 字符串文字中使用这些字符时,它们都不需要特殊处理。

但是说真的,当有提供参数化/准备语句等便捷技术的库和框架时,为什么会有人想要使用这些繁琐的格式化技术?

【讨论】:

【解决方案2】:

虽然我不熟悉 hexing,但我过去曾成功使用 Base64 防止各种脚本注入 mysql。

【讨论】:

  • 听起来很像同样的方法——你用来解码base64的MySQL函数是什么? (伪代码示例?)
  • 我以前从未直接在 MySQL 中做过。我通常使用 PHP 的 base64 编码和解码函数,但是我在网上查找时发现了这个:stackoverflow.com/questions/358500/base64-encode-in-mysql
  • 嗯,这很有趣。不知道我的生产环境是否会在 MySQL>=5.6 上,但这绝对是要考虑的事情。可以帮助减少查询大小(十六进制看起来相当臃肿)。
  • Base64 将信息(字符串)的大小增加了大约 33%,但具有混淆任何输入并且不需要特定数据编码(意味着通常需要 UTF-没有它仍然可以存储8个)
  • 这是一个非常好的观点,那么任何字符编码都由 php 专门处理。我认为将数据库中的内容存储为 base64 字符串的主要缺点是按字母顺序对它们进行排序,比如姓氏或其他东西(在 phpMyAdmin 中,或者你有什么)是无效的。
【解决方案3】:

这种类型的安全性是否存在漏洞?

没有漏洞,但也没有优势。传统的字符串格式同样安全,但没有所有这些无用的十六进制/取消十六进制的东西。

所以,用这些非十六进制的东西来膨胀你的查询是多余的。

截至另一个答案,最初它是在没有取消十六进制的情况下编写的,因此以数字失败,这使得它无法使用。而在添加 unhex 之后,它变得毫无用处和多余。

【讨论】:

    【解决方案4】:
    $query="SELECT * FROM table WHERE someidentifier=UNHEX('".bin2hex($unsafe_user_input)."') LIMIT 1"
    

    这是使用 PHP 和 MySQL 防止 SQL 注入的简单而确定的答案。

    我已经看到 cmets 表明仍然可以通过准备好的或参数化的 SQL 对 SQL 注入开放。当这个解决方案简单且有效时,我认为没有任何理由对此大惊小怪。

    我已经看到关于 CPU 时间和内存使用的争论,但由于这通常用于在屏幕上输入的用户输入,他们关心 RAM 使用或 CPU 周期(喘气!)。用户输入 500 个字符,十六进制版本是一千个字符。除非您的服务器有十万人在同一时间执行此操作,否则您不会看到太大的差异。

    创建清晰、明显、可维护的代码值得几个 CPU 周期和一些 RAM。

    而且技术很简单,很容易记住。随着 PHP 的不断发展,它也不太可能打破或开发漏洞。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多