PHP's(已弃用)mysql模块与MySQLi &pdo


Vulnerabilities of PHP's (deprecated) mysql module vs. MySQLi & PDOs

我负责维护和扩展一个PHP代码库,该代码库始于2007年,使用原始的mysql模块。所有用户输入都使用数值转换进行转义,mysql_real_escape_string()对字符串使用单引号引用,可能通过in_array()ENUM字段或array_intersect()SET字段进一步过滤。在输出HTML时,所有不受约束的字符串字段都将通过htmlspecialchars()htmlentities()传递。如果一个值代表一个外键,则首先验证该键是否存在。
我相信,通过严格遵循这些程序,应用程序是安全的,因为它可以防止注入和其他形式的攻击。(加分:我说的对吗?如果没有,我错过了什么?)

将此应用程序转换为mysqli或PDOs将是一个相当大的任务(并且,为了避免意外破坏,不是我想要自动化的东西)。所以最后我的问题:是否有任何特定的漏洞,不能减轻使用旧的mysql模块,这需要迁移到较新的模块?

赏金信息:
为了明确,我希望PHP开发人员能够提供CVE编号列表或声明,说明mysql模块已针对诸如此类的所有已知漏洞进行了修补。我还假设在使用该模块时遵循最佳当前实践不会使我暴露于其他攻击向量。bcp已经包括在将数据插入新语句之前对从db获取的数据进行转义。不停地说这些并不能真正解决问题。

我只有两点异议

  • All user input is escaped为临界故障,导致二次注入。"SQL的所有动态数据"是正确的方法和措辞
  • 你的帖子中没有提到任何标识符,但我不敢相信自2007年以来你的代码中没有动态标识符的查询。

还有一个小麻烦:几年后(可能3-4年),你的PHP将开始发出E_DEPRECATED级别的错误。但它们可以被简单地关闭。

无论如何,仅仅从一个API到另一个API的机械移动不会有太大的意义。
重构你的SQL处理代码,只利用一些抽象机制,无论是ORM, AR, QueryBuilder还是其他任何技术,将从应用程序代码中删除原始API调用。它不仅可以使您的代码不那么臃肿,而且还可以使它独立于将来PHP开发人员的任何突发奇想。

回答编辑后的问题。

旧mysql ext中没有必要的漏洞,唯一常用的方式是容易受到攻击和容易出错。
因此,与其在模块上寻找压力,不如审计代码。如果它没有使用集中式库来利用准备好的语句进行数据库交互,那么它很可能是脆弱的。

我的答案可能会有些偏离,而不是回答你的具体问题,我宁愿建议一种能真正帮助你的方法。

无论什么漏洞可能会留下或可能出现在未来使用mysql,无论你目前的代码库方法(而不是避免sqlinject)有多可靠(它似乎做得很好,虽然),我得到的感觉是,你仍然宁愿迁移到mysqli,但目标是延迟这样做,通过看到短期的可能性,进一步破解你的方式,不安全的和完全弃用的mysql。

我建议重构。时期。就这么做吧。记住要进行重构,而不是在这样做时更改或扩展代码库——这是一个令人讨厌的陷阱。尽管这需要一些工作——重构,只要开始你的重构过程(当然是分支的)。完成后会非常满意。预计会出现一些长尾问题。

我假设你描述的每一个功能都已经包装好了,所以重构应该是相当可行的。如果东西没有被包装……($#@!),找出一种在项目范围内唯一跟踪函数调用(包括上下文)的方法,(可能使用正则表达式来查找它们)并替换为新的唯一要使用的包装函数。首先,彻底地探索一下这个问题。在半天的时间内,您将能够获得所需的所有正则表达式。所以先计划好,先探索你的道路。

用当前的(旧的)功能代码填充新的包装器,看看是否仍然可以正常工作。

然后开始迁移到mysql,并在内部重新构建您的包装器。

似乎是一种尽可能简单的方法,避免了所有的事情和问题,将留在你的脑海里,尽管你试图在破解你的方式更深的普通mysql。我不需要告诉你我将带来的好处,你已经知道了。此外,它只是一个很好的实践,每隔一段时间实际上承担这些问题一劳永逸。计划、讨论、分支、尝试、执行、测试、完成和征服!最重要的是:确保在重构时不要错过扩展代码库和功能范围的机会——你可能会忍不住这么做:只要重构就好。以后添加、扩展或改进

我觉得我没有资格明确地回答你的问题,所以请小心使用我提供的信息,但是我确实有一些管理你可能正在看的那种变化的经验。然而,如果你说的是真的,你的语句应该保护你免受XSS和SQL注入。

我最近开始将整个大型应用程序从mysql_迁移到mysqli_,在这样做的过程中,我还设置了一个目标,即所有用户输入都应该通过准备好的语句。

编辑:为了避免歧义,我总是把用户通过准备好的语句生成的任何东西放在一起,即使它已经存储在数据库中。在可能的情况下,我尽量避免重新插入用户数据,因为它是不可靠的,所以系统功能往往依赖于自动生成的索引。

公平地说,这些页面很短(不超过1000行),所以每页的修复时间不长,而且它们有很少的查询,所以性能降低并不明显,而且肯定是由于我编写原始代码以来服务器技术的改进而抵消的。我怀疑你会发现在转义等方面的减少将远远超过对prep语句的性能影响,尽管你必须检查它是否至关重要。

这让我感到沮丧的是,在做这个审查时,我在代码中发现了多少漏洞(我在编写时尽可能地包括安全性,并为自己设定了与您相同的规则),最终我发现需要重写大块代码以提高安全性。性能有了显著的提高,这也是体验和代码调整的结果。

我要改变的方式是在我的数据库头文件中添加一个mysqli连接。所有新代码都使用这个。随着时间的推移,我正在更新旧代码,并使用没有旧mysql连接的头文件测试每个页面。在开发环境中,用这种方法可以很快找到你错过的部分,这是利用时间的好方法,否则每个页面只需要几分钟的更新,所以可以在大脑衰退期间完成。

关于二阶注入的注意事项,因为这是我构建的最常见的漏洞:

大多数SQL注入防御都假设注入攻击只发生在登录时,来自恶意的非注册用户,一旦被阻止就永远不会返回,并且注册用户是可以信任的。不是这样。

可以想象,代码可以通过你的保护被注入,然后被使用。这是最不可能工作的,因为它严重依赖于笨拙的数据库和应用程序设计,但是世界上一些最聪明的人是黑客。为什么要让他们的生活更轻松?

如果您的sql很简单,并且您的应用程序使用先前从db获得的数据进行任何子查询,则攻击更有可能发生。记住
' OR 1=1 gets converted to
'' OR 1=1 by mysql_real_escape_string but is stored as 
' OR 1=1 in the db 
因此,如果检索到

并将其放入PHP变量中,然后在sql查询中使用未转义的

,则可能导致问题,就像从未转义一样。如果您只对所有查询使用prep语句,那么风险将永久消失,但请记住prep语句仍然会存储恶意代码,因此当您下次需要使用已输入的数据时,您仍然需要再次使用prepare语句。

这个博客给出了一个很好的讨论和例子,所以我不会进一步展开,但是如果你确保所有用户输入的数据都是通过准备好的语句传递的,如果它被用作查询的一部分,即使它已经存储在数据库中,你应该是安全的。

冒着重复的风险,与OWASP站点保持友好关系也是值得的,因为它有很有价值的安全讨论。

好的,所以我做了一点研究,我发现关于mysql扩展的唯一漏洞似乎也同样影响mysqli扩展是CVE-2007-4889这是一个"安全模式绕过"漏洞,并已修复很久以前更重要的是,这两个mysql。我也是。所以模块共享了几乎相同的导入-

/usr/lib/php5/20090626/mysql.so:

 0x0000000000000001 (NEEDED)             Shared library: [libmysqlclient.so.18]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000e (SONAME)             Library soname: [mysqli.so]

/usr/lib/php5/20090626/mysqli.so:

 0x0000000000000001 (NEEDED)             Shared library: [libmysqlclient.so.18]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000e (SONAME)             Library soname: [mysql.so]

由于两个模块的共享导入特性,也有可能在两个模块中出现新的漏洞而且总是有可能一个独特的缺陷源自其中一个模块的实际代码。但到目前为止,这两个模块的漏洞记录似乎几乎相同

正如这里提到的,我会花更多的时间来审计PHP源代码,以确保以下几点——

  1. SQL注入-参数化SQL查询是针对此类缺陷的最佳解决方案。

  2. XSS(跨站脚本)-使用htmlspecialchars()过滤危险字符

  3. CSRF(跨站点请求伪造)-始终对表单执行引用检查,以确保数据从正确的位置到达

  4. 文件包含-确保没有用户输入会直接影响文件的包含(require(), require_once(), include(), include_once())

  5. File Upload -如果由于某种原因启用了文件上传,请确保不要让用户控制文件扩展名或保存文件并将其权限设置为"非执行"。这样做是为了防止攻击者上传恶意文件并在您的服务器上执行代码。