10.8. PHP

SecureReality 发布了一篇非常有趣的论文,题为“深红研究 - 利用 PHP 中的常见漏洞”[Clowes 2001],其中讨论了使用 PHP 编写安全程序时的一些问题,尤其是在 PHP 4.1.0 之前的版本中。Clowes 总结说,“即使你尝试,也很难编写一个安全的 PHP 应用程序(在 PHP 的默认配置中)”。

诚然,任何语言都存在安全问题,但 PHP 早期版本中一个特别突出的问题,可以说使早期 PHP 版本比大多数语言都更不安全:它将数据加载到其命名空间的方式。默认情况下,在 PHP(4.1.0 及更低版本)中,所有环境变量和通过网络发送到 PHP 的值都会自动加载到与普通变量加载到的相同命名空间(全局变量)中 - 因此攻击者可以设置任意变量为任意值,这些值会一直保留,除非 PHP 程序显式重置它们。此外,PHP 在首次请求变量时会自动创建具有默认值的变量,因此 PHP 程序通常不会初始化变量。如果你忘记设置变量,PHP 可以报告它,但默认情况下 PHP 不会 - 请注意,这只是一个错误报告,它不会阻止发现异常方法来触发它的攻击者。因此,默认情况下,PHP 允许攻击者完全控制程序中所有变量的值,除非程序特别注意覆盖攻击者。一旦程序接管,它可以重置这些变量,但未能重置任何变量(即使是不明显的变量)可能会在 PHP 程序中打开一个漏洞。

例如,以下 PHP 程序(来自 Clowes 的示例)旨在仅让知道密码的人获取一些重要信息,但攻击者可以在他们的网络浏览器中设置“auth”并破坏授权检查
 <?php
  if ($pass == "hello")
   $auth = 1;
  ...
  if ($auth == 1)
   echo "some important information";
 ?>

我和许多其他人抱怨过这个特别危险的问题;这是一个特别的问题,因为 PHP 被广泛使用。一种本应易于使用的语言最好能使其易于编写安全程序,毕竟。可以通过将设置“register_globals”设置为“off”来禁用 PHP 中的此错误功能,但默认情况下,PHP 4.1.0 及更早版本将此设置为“on”,并且在 register_globals 关闭的情况下,PHP 4.1.0 之前的版本更难使用。PHP 开发人员在其 PHP 4.1.0 公告中警告说,“从 PHP 的下一个半主要版本开始,PHP 的新安装将默认将 register_globals 设置为 off。” 现在已经发生了;从 PHP 4.2.0 版本开始,外部变量(来自环境、HTTP 请求、cookie 或 Web 服务器)默认不再在全局作用域中注册。访问这些外部变量的首选方法是使用 PHP 4.1.0 中引入的新超级全局数组。

对于重要的程序来说,将“register_globals”设置为“on”的 PHP 是一个危险的选择 - 它太容易编写不安全的程序了。但是,一旦将“register_globals”设置为“off”,PHP 就成为一种相当合理的开发语言。

安全的默认设置应包括将“register_globals”设置为“off”,并包括几个功能,以便用户更容易指定和限制他们将接受来自外部源的输入。然后 Web 服务器(例如 Apache)可以单独配置此安全的 PHP 安装。可以将例程放在 PHP 库中,以便用户轻松列出他们想要接受的输入变量;一些函数可以检查这些变量必须具有的模式和/或变量必须强制转换成的类型。在我看来,如果你将 register_globals 设置为 on,PHP 对于安全的 Web 开发来说是一个糟糕的选择。

正如我在本书早期版本中建议的那样,PHP 已经过简单的修改,成为安全 Web 开发的合理选择。但是,请注意,PHP 没有特别好的安全漏洞记录(例如,register_globals、文件上传问题和错误报告库中的格式字符串问题);我认为在早期版本的 PHP 中,安全问题没有得到充分考虑;我也认为 PHP 开发人员现在正在强调安全性,并且这些安全问题最终正在得到解决。一个证据是 PHP 开发人员为了关闭 register_globals 所做的重大更改;这对 PHP 用户产生了重大影响,他们愿意做出这种改变是一个好兆头。不幸的是,PHP 到底有多安全尚不清楚;现在 PHP 开发人员正在认真检查其安全性问题,PHP 还没有多少记录。希望这一点很快就会变得清晰。

如果你已决定使用 PHP,以下是我的一些建议(其中许多建议基于应对 Clowes 提出的问题的方法)