12

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

将此应用程序转换为mysqliPDO 将是一项相当大的任务(并且,为了避免意外损坏,我不想自动化)。所以最后我的问题是:使用旧模块时是否有任何特定漏洞无法缓解mysql,需要迁移到新模块?

赏金信息:
明确地说,我希望得到一份 CVE 编号列表或 PHP 开发人员的声明,即该mysql模块已针对某某日期的所有已知漏洞进行了修补。我还假设在使用该模块时遵循最佳当前实践不会使我暴露于其他攻击媒介。BCP 已经包含在将数据插入新语句之前从数据库中提取的转义数据。不断地讨论这个问题并不能真正解决这个问题。

4

4 回答 4

7

我只有两个反对意见

  • All user input is escaped是一个严重故障,导致二阶喷射。“SQL 的所有动态数据”是正确的方法和措辞
  • 您的帖子中没有提到任何标识符,但我不敢相信自 2007 年以来您的代码中没有包含动态标识符的查询。

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

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

回答编辑后的问题。

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

于 2013-03-18T09:25:46.867 回答
2

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

无论将来使用 mysql 可能留下或可能出现什么漏洞,也无论您当前的代码库方法多么可靠(而不是避免)SQLinjection(不过,它似乎做得很好),我觉得您仍然会无论如何,宁愿迁移到 mysqli,但目标是通过查看短期可能性来推迟这样做,以进一步破解不安全和完全弃用的 mysql。

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

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

用当前(旧的)功能代码填充新的包装器,看看是否一切都照常工作。

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

似乎是一种尽可能简单的方法,避免了所有将留在脑海中的问题和问题,尽管您尝试做任何事情来深入了解常见的 mysql。我不需要告诉你mysqli会带来什么好处,你已经知道了。此外,每隔一段时间实际一劳永逸地处理这类问题,这只是一种很好的做法。计划、讨论、分支、尝试、做、测试、完成和征服!最重要的是:确保您在重构时不会错过扩展代码库和功能范围的机会 - 您会很想这样做:只需重构即可。稍后添加、扩展或改进。

于 2013-08-06T19:55:28.233 回答
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 注入 -参数化 SQL查询是针对此类缺陷的最佳解决方案。

  2. XSS(Cross Site Scripting) - 使用 htmlspecialchars() 过滤危险字符

  3. CSRF(跨站点请求伪造) - 始终对表单执行推荐人检查,以确保数据来自正确的位置

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

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

于 2013-08-06T15:20:33.757 回答
1

我觉得没有资格明确回答您的问题,因此请谨慎使用我提供的信息,但我在管理您可能正在查看的更改方面确实有一些经验。AFAICS 但是,如果您说的是真的,您的语句应该保护您免受 XSS 和 SQL 注入。

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

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

平心而论,页面很短(不超过 1000 行),因此修复每个页面不需要很长时间,并且它们几乎没有查询,因此性能下降并不明显,而且自从我编写原始代码以来,服务器技术的改进肯定会吃掉. 我怀疑您会发现转义等的减少将远远超过对准备语句的任何性能影响,尽管您必须检查它是否至关重要。

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

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

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

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

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

如果您的 sql 很简单并且您的应用程序使用先前从数据库中获得的数据进行任何子查询,则攻击的可能性更大。请记住

' 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 语句仍将存储恶意代码,因此当您下次需要使用已输入的数据时,您仍然必须再次使用准备好的语句。

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

冒着重复的风险,对OWASP 站点变得非常友好也是值得的,该站点具有有价值的安全讨论。

于 2013-08-04T10:41:23.953 回答