首页 >后端开发 >php教程 >框架和 CMS 中奇怪的 PHP 代码

框架和 CMS 中奇怪的 PHP 代码

Patricia Arquette
Patricia Arquette原创
2024-11-14 20:45:02840浏览

That Strange PHP Code in Frameworks and CMSs

注意:为了阅读这篇文章,假设您具有一些 PHP 编程的基本知识。

本文讨论了您可能在您最喜欢的 CMS 或框架顶部看到的 PHP 代码片段。您可能已经读过,出于安全原因,您应该始终将它包含在您开发的每个 PHP 文件的开头,尽管没有非常清楚地解释原因。我指的是这段代码:

<?php

if ( ! defined( 'ABSPATH' ) ) {
    exit; // Exit if accessed directly
}

这种类型的代码在 WordPress 文件中很常见,尽管它出现在几乎所有框架和 CMS 中。例如,对于 Joomla CMS,唯一的变化是它使用 JEXEC,而不是 ABSPATH。除此之外,逻辑保持不变。这个 CMS 是从之前的一个名为 Mambo 的系统发展而来的,它也使用了类似的代码,但以 _VALID_MOS 作为常量。如果我们再往前追溯,我们会发现第一个使用此类代码的 CMS 是 PHP-Nuke(被一些人认为是第一个基于 PHP 的 CMS)。

PHP-Nuke(以及当今大多数 CMS 和框架)的执行流程包括顺序加载多个文件以响应用户或访问者在网站上采取的操作。例如,想象一下那个时代的网站托管在 example.net 并安装了此 CMS。每次加载主页时,系统都会按顺序执行一系列文件(这只是一个示例,不是实际的顺序):index.php => load_modules.php =>;模块.php。在这个链中,首先加载index.php,然后加载load_modules.php,然后加载modules.php。

这个执行链并不总是从第一个文件(index.php)开始。事实上,任何人都可以通过其 URL(例如,http://example.net/load_modules.php 或 http://example.net/modules.php)直接访问其他 PHP 文件之一来绕过部分流程。 ,正如我们将看到的,这在许多情况下可能存在风险。

这个问题是如何解决的?引入了安全措施,在每个文件的开头添加类似的代码:

<?php

if (!eregi("modules.php", $HTTP_SERVER_VARS['PHP_SELF'])) {
    die ("You can't access this file directly...");
}

本质上,这段代码放置在名为modules.php的文件的顶部,检查是否可以通过URL直接访问modules.php。如果是,则停止执行,显示消息:“You can't access this file direct...” 如果 $HTTP_SERVER_VARS['PHP_SELF'] 不包含modules.php,则意味着正常执行流程处于活动状态,允许脚本继续。

但是,此代码有一些限制。首先,插入代码的每个文件的代码都不同,这增加了复杂性。另外,在某些情况下,PHP 没有为 $HTTP_SERVER_VARS['PHP_SELF'] 赋值,这限制了其有效性。

那么,开发者做了什么?他们用更简单、更高效的版本替换了所有这些代码片段:

<?php

if ( ! defined( 'ABSPATH' ) ) {
    exit; // Exit if accessed directly
}

在 PHP 社区中非常流行的这段新代码中,检查了常量是否存在。该常量在执行流的第一个文件(index.php、home.php 或类似文件)中定义并赋值。因此,如果该常量不存在于序列中的任何其他文件中,则意味着有人绕过了index.php并试图直接访问另一个文件。

直接运行 PHP 文件的危险

此时,你可能会想,破坏执行链一定是极其严重的事情。然而,事实是,通常,它不会构成重大威胁。

当 PHP 错误暴露我们文件的路径时,可能会出现风险。如果服务器配置为抑制错误,那么这不应该让我们担心;即使没有隐藏错误,暴露的信息也很少,只能为潜在攻击者提供一些线索。

也可能有人访问包含 HTML 片段(视图)的文件,从而泄露其部分内容。在大多数情况下,这也不必担心。

最后,开发人员可能会因错误或缺乏经验而将没有外部依赖项的有风险的代码放置在执行流程中。这种情况非常罕见,因为框架或 CMS 代码通常依赖于其他类、函数或外部变量来执行。因此,如果尝试直接通过 URL 执行脚本,则会出现错误,因为找不到这些依赖项,并且执行将无法继续。

那么,如果没有什么理由担心的话,为什么要添加常量代码呢?答案是这样的:“此方法还可以防止通过 注册全局变量 攻击进行意外变量注入,从而防止 PHP 文件假设它位于应用程序中,而实际上它并不在应用程序中。”

注册全局变量

自 PHP 早期以来,所有通过 URL (GET) 或表单 (POST) 发送的变量都会自动转换为全局变量。例如,如果访问了 download.php?filepath=/etc/passwd 文件,则在 download.php 文件中(以及在执行流程中依赖于该文件的文件中),您可以使用 echo $filepath;它会输出 /etc/passwd。

在 download.php 中,无法知道变量 $filepath 是否由执行链中的先前文件创建,或者是否通过 URL 或 POST 被篡改。这造成了严重的安全漏洞。让我们看一个示例,假设 download.php 文件包含以下代码:

<?php

if ( ! defined( 'ABSPATH' ) ) {
    exit; // Exit if accessed directly
}

开发人员可能打算在其代码中使用前端控制器模式,这意味着所有 Web 请求都将通过单个入口文件(index.php、home.php 等)。该文件将处理会话初始化、加载公共变量,最后将请求重定向到特定脚本(在本例中为 download.php)以执行文件下载。

但是,如前所述,攻击者只需调用 download.php?filepath=/etc/passwd 即可绕过预期的执行序列。 PHP 会自动创建全局变量 $filepath,其值为 /etc/passwd,从而允许攻击者从系统下载该文件。严重问题。

这只是冰山一角,因为甚至可以用最小的努力来执行更危险的攻击。例如,在如下代码中,程序员可能将其保留为未完成的脚本:

<?php

if (!eregi("modules.php", $HTTP_SERVER_VARS['PHP_SELF'])) {
    die ("You can't access this file directly...");
}

攻击者可以通过使用远程文件包含(RFI)攻击来执行任何代码。如果攻击者在自己的网站 https://mysite.net 上创建了一个文件 My.class.php,其中包含他们想要执行的任何代码,他们可以通过传入其域来调用易受攻击的脚本:unusable_code.php?base_path=https: //mysite.net,攻击就完成了。

另一个示例:在名为remove_file.inc.php 的脚本中,包含以下代码:

<?php

if (!defined('MODULE_FILE')) {
    die ("You can't access this file directly...");
}

攻击者可以使用像remove_file.inc.php?filename=/etc/hosts这样的URL直接调用这个文件,试图从系统中删除/etc/hosts文件(如果系统允许的话),或者他们想要删除的其他文件。有删除权限)。

在像 WordPress 这样内部也使用全局变量的 CMS 中,这些类型的攻击是毁灭性的。然而,由于不断的技术,这些和其他 PHP 脚本受到了保护。让我们看最后一个例子:

<?php

if(file_exists($filepath)) {
    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename="'.basename($filepath).'"');
    header('Expires: 0');
    header('Cache-Control: must-revalidate');
    header('Pragma: public');
    header('Content-Length: ' . filesize($filepath));
    flush(); // Flush system output buffer
    readfile($filepath);
    exit;
}

现在,如果有人尝试访问remove_file.inc.php?filename=/etc/hosts,该常量将阻止访问。这是一个常量,这一点很重要,因为从逻辑上讲,如果它是一个变量,攻击者就可以注入它。

现在,您可能想知道如果 PHP 如此危险,为什么还要保留此功能。另外,如果您了解其他脚本语言(JSP、Ruby 等),您会发现它们没有任何相似之处(这就是为什么它们也不使用常量技术)。回想一下,PHP 最初是作为基于 C 的模板系统创建的,这种行为使开发变得更加容易。好消息是,看到它造成的问题,PHP 维护者引入了一个名为 register_globals(默认启用)的 php.ini 指令,以允许禁用此功能。

但由于问题仍然存在,他们默认禁用它。即便如此,许多主机仍然启用它,因为担心客户的项目会停止工作,因为当时的大部分代码没有使用推荐的 HTTP_*_VARS 变量来访问 GET/POST/... 值,而是使用全局变量。

最后,看到情况没有改善,他们做出了一个重大决定:在 PHP 5.4 中删除此功能以避免所有这些问题。因此,今天,除了在某些情况下出现一些无害的警告/通知之外,像我们所看到的脚本(不使用常量)通常不再是风险。

当前使用情况

时至今日,持续技术仍然很常见。然而,不幸的现实——也是本文的原因——是很少有开发人员了解其使用背后的真正原因。

与过去的其他最佳实践一样(例如将参数复制到函数内部的局部变量中以避免引用问题或在私有变量中使用下划线来区分它们),许多人继续应用它只是因为有人曾经告诉他们这是一个良好的做法,毫无疑问它今天是否仍然增加价值。事实是,在大多数情况下,不再需要此技术。

以下是这种做法失去相关性的一些原因:

  • 删除 *register 全局变量:从 PHP 5.4 开始,在 PHP 中删除了将 GET 和 POST 变量自动注册为全局变量的功能。如果没有*注册全局变量,直接执行单个脚本是无害的,消除了这种技术的主要原因。

  • 更好的代码设计:即使在 PHP 5.4 之前的版本中,现代代码的结构也更好,通常在类和函数中,这使得通过外部变量进行访问或操作更具挑战性。即使是传统上使用全局变量的 WordPress,也能最大限度地降低这些风险。

  • *front-controllers的使用:如今,大多数 Web 应用程序都采用精心设计的 *front-controllers 来确保类和函数代码仅在执行时才执行链从主入口点开始。因此,如果有人尝试单独加载文件,除非流程从正确的入口点开始,否则逻辑不会触发。

  • 类自动加载:随着类自动加载在现代开发中的广泛使用,include 或 require 的使用显着减少。这可以降低经验丰富的开发人员与这些方法(例如远程文件包含本地文件包含)相关的风险。

  • 公共代码和私有代码的分离:在许多现代 CMS 和框架中,公共代码(如 资产)与私有代码(逻辑)分离。这一措施特别有价值,因为它可以确保,如果 PHP 在服务器上发生故障,PHP 代码(无论是否使用常量技术)不会暴露。尽管这种分离并不是专门为了缓解注册全局变量而实现的,但它有助于防止其他安全问题。

  • 友好 URL 的广泛使用:如今,将服务器配置为使用友好 URL 是常见做法,以确保应用程序逻辑的单一入口点。这使得任何人几乎不可能单独加载 PHP 文件。

  • 生产中的错误抑制:大多数现代 CMS 和框架默认禁用错误输出,因此攻击者无法找到有关应用程序内部工作原理的线索,这可能会促进其他类型的攻击。

尽管在大多数情况下不再需要此技术,但这并不意味着它永远没有用处。作为一名专业开发人员,必须分析每种情况并确定持续的技术是否与您工作的特定环境相关。这种批判性思维应该始终被应用,即使是所谓的最佳实践。

没有把握?这里有一些提示

如果您仍然不确定何时应用持续技术,这些建议可能会指导您:

  • 如果您认为您的代码可能在早于 5.4 的 PHP 版本上运行,请始终使用它
  • 如果文件仅包含类定义,请不要使用它
  • 如果文件仅包含函数,请不要使用它
  • 如果文件仅包含 HTML/CSS,请勿使用它,除非 HTML 泄露敏感信息。
  • 如果文件仅包含常量,请不要使用它

对于其他一切,如果您有疑问,请应用它。在大多数情况下,它不会有害,并且可以在意外情况下保护您,尤其是在您刚开始时。随着时间和经验的积累,您将能够评估何时更有效地应用此技术和其他技术。

That Strange PHP Code in Frameworks and CMSs

继续学习...

  • register_globals - MediaWiki
  • PHP:使用寄存器全局变量 - 手册
  • 远程文件包含漏洞 [LWN.net]
  • Bugtraq:Mambo Site Server 版本 3.0.X 中存在严重安全漏洞

以上是框架和 CMS 中奇怪的 PHP 代码的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn