PHP(已弃用)mysql模块与MySQLi&的漏洞原产地名称

时间:2013-03-18 09:07:57

标签: php mysql web-services security sql-injection

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

将此应用程序转换为mysqli或PDO将是一项相当大的任务(并且,为避免意外破坏,我不希望自动化)。最后我的问题是:使用旧的mysql模块时是否存在无法缓解的特定漏洞,这需要迁移到较新的模块?

赏金信息:
为了清楚起见,我希望得到一个CVE编号列表或PHP开发人员的声明,mysql模块将针对所有已知漏洞进行修补。我也假设在使用模块后遵循最佳实践并不会让我接触到额外的攻击向量。 BCP在将数据插入新语句之前已经包含从数据库中获取的数据。继续谈论这个并没有真正解决这个问题。

4 个答案:

答案 0 :(得分:7)

我只有2个异议

  • All user input is escaped是一个严重错误,导致second order injection。 “SQL的所有动态数据”都是正确的方法和措辞
  • 您的帖子中没有提及identifiers,但我不相信自2007年以来您的代码中没有动态标识符查询。

还有一个小小的不便:在几年内(可能3-4个),您的PHP将开始发出E_DEPRECATED级错误。但他们可以简单地关闭。

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

回答编辑过的问题。

旧的mysql ext中没有必要的漏洞。它常用的唯一方式是易受攻击且容易出错。
因此,不要在模块上寻找压力,而是更好地审核您的代码。如果它没有使用集中式库来利用预备语句进行数据库交互,则很可能是易受攻击的。

答案 1 :(得分:2)

我的回答有点偏离,而不是回答你的具体问题,我宁愿建议实际上会帮助你的方法。

无论将来使用mysql可能会留下什么样的漏洞,或者无论你当前的代码库如何接近(相当避免)SQLinjection(尽管看起来这样做很好),我感觉很好无论如何你仍然宁愿迁移到mysqli,但是通过看到短期的可能性来进一步破解你对不安全和被完全弃用的mysql的方法,目的是推迟这样做。

我建议重构。期。就这样做。请记住重构,而不是在这样做时更改或扩展您的代码库 - 这是一个令人讨厌的陷阱。虽然它将是一些工作 - 重构,只是开始你的重构过程(当然分支)。完成它将非常令人满意。期待一些长尾问题。

我假设您描述的每个功能都已包装,因此重构应该是相当可行的。如果事情没有被包装..($#@!),找出一种方法来在项目范围内唯一地跟踪你的函数调用(包括上下文),(可能使用正则表达式来查找它们)并用新的唯一替换 - 使用包装函数。首先要彻底探索这个。在半天内,您将能够获得所需的所有正则表达式。因此,首先要通过探索自己的路径进行规划。

使用当前(旧)功能代码填充新包装器,看看是否仍然按原样运行。

然后开始迁移到mysqli,并在内部重建包装器。

似乎是一种尽可能简单的方法,避免所有的问题和问题留在你的脑海中,尽管你试图通过黑客攻击普通的mysql更深入。我不需要告诉你mysqli带来的好处,你已经知道了。此外,在每次实际承担这些问题时,每次都是好的做法。计划,讨论,分支,尝试,做,测试,完成和征服!最重要的是:确保你没有机会在重构时扩展你的代码库和功能范围 - 你很想这样做:只是重构。稍后添加,扩展或改进。

答案 2 :(得分:1)

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

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

编辑以回应下面的YCs公平评论:为了避免歧义,我总是通过准备好的声明放置用户生成的任何内容,即使它已经存储在数据库中。尽可能避免使用重新插入用户数据,因为它不可靠,因此系统功能往往依赖于自动生成的索引。

公平地说,页面很短(不超过1000行),因此每页修复时间不长,查询很少,因此性能降低并不明显,并且自从我写完以后,服务器技术的改进肯定会使用它们原始代码。我怀疑你会发现逃避等的减少将远远超过预备陈述中的任何表现,尽管你必须检查它是否至关重要。

在我进行此次审核时,我在代码中发现了多少漏洞让我感到沮丧(我在编写时尽可能地保护了安全性并将自己的规则设置为与您的相同)最终我发现需要重写大代码块以提高安全性。由于更多的经验和代码调整,性能也得到了显着提升。

我正在进行更改的方法是添加mysqli连接到我的数据库头文件。所有新代码都使用此代码。我找时间,我正在更新旧代码并使用没有旧mysql连接的头文件测试每个页面。在开发环境中找到你错过的那些东西很快就会死掉它,这可能是一个非常好的方式来使用时间,否则会浪费时间,因为每个页面只需要几分钟的时间来更新,所以可以在脑褪色期间完成周期。

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

大多数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语句仍将存储恶意代码,因此您在下次需要使用已输入的数据时仍需要再次使用预准备语句。

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

冒着重复的风险,值得对OWASP site非常友好,{{3}}有宝贵的安全性讨论。

答案 3 :(得分:1)

好的,所以我做了一些研究,我发现的关于mysql扩展的唯一漏洞似乎也影响了mysqli同样的扩展名为CVE-2007-4889  这是一个“安全模式绕过”漏洞,并且很久以前就已修复 更多的是mysql.so和mysqli.so模块共享的进口数量几乎相同 -

/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注入 - Parametrized SQL查询是解决此类漏洞的最佳解决方案。

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

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

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

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