【问题标题】:How to detect if a file is being included or directly ran如何检测文件是否被包含或直接运行
【发布时间】:2011-04-26 22:21:19
【问题描述】:

我有一个包含在我的 php 脚本中的 php 文件,但我不希望人们能够直接运行该文件(不包含在内)。 我怎样才能防止这种情况发生?

【问题讨论】:

    标签: php include


    【解决方案1】:

    使包含的脚本根本无法通过 HTTP 访问。例如。通过保护子文件夹或将它们移动到文档根目录上方。

    如果你不能这样做,define() 类似于 IS_INCLUDED 在你的主脚本中,exit; 如果这个常量不是 defined() 在你包含的脚本中。

    【讨论】:

    • 我想很多 webapps 选择后一个选项 soley 是为了简化安装。如果可以的话,将包含的脚本保留在 docroot 之外绝对是一个更好的解决方案。
    • .htaccess 的解决方案也很简单——而且大多数人都在使用 Apache。
    • 对于第一个解决方案,如果您不实施第二个解决方案,它可能会很危险。如果您在共享环境中,则服务器可以更改其配置(也错误地),从而使子文件夹保护不再起作用,或者您可以将脚本移动到具有不同安全策略的另一台服务器上,您可能不会对此做好充分准备。更好地编写强大的代码,而不是依赖某些服务器功能。
    • 但是,如果有人关闭 AllowOverride,.htaccess 不会帮助您。
    • 我认为如果 AllowOverride 关闭,您会收到 500 错误。所以它也会有效地把人拒之门外。 @Giacomo:这就像期望 PHP 被意外禁用,导致人们能够查看您的 PHP 源代码,包括数据库登录数据......
    【解决方案2】:

    检查脚本是否是 PHP 进程的父进程可能不是阻止用户直接请求包含文件的最佳方法。但它在许多其他情况下也很方便,例如 AJAX 模块等。我不会以此开始新话题。

    if (__FILE__ == get_included_files()[0])
    // Doesn't work with PHP prepend unless calling [1] instead.
    
    if (__FILE__ == $_SERVER['DOCUMENT_ROOT'] . $_SERVER['SCRIPT_FILENAME'])
    // May not work on Windows due to mixed DIRECTORY_SEPARATOR (machine specific)
    
    if (basename(__FILE__) == basename($_SERVER['SCRIPT_FILENAME']))
    // Doesn't work with files with the same basename but different paths
    
    if (defined('FLAG_FROM_A_PARENT'))
    // Works in all scenarios but I personally dislike this
    
    if (realpath(__FILE__) == realpath($_SERVER['DOCUMENT_ROOT'] . $_SERVER['SCRIPT_FILENAME']))
    // Should work flawlessly
    

    请记住,某些使用虚拟路径的机器可能会在不同的 php 组件中返回不同的路径。 realpath() 是你的朋友。

    【讨论】:

    • 我已经在windows中成功使用了if(strtolower($_SERVER['SCRIPT_FILENAME']) === strtolower(__FILE__)) {版本。当我想测试一个我通常需要_once() 的文件时,我会使用它。
    • 在您的第二个选项中,script_filename 变量中已经包含了整个路径 + 文件名。不知道其他人是不是也这样......
    【解决方案3】:

    我使用一个 sentinel 常量,在“包含”脚本中定义,“包含”脚本会查找它。

    例子:

    /* main.php */
    define('SENTINEL', 1);
    include "included.inc.php"
    
    /* included.inc.php */
    if (!defined('SENTINEL')) die("Wanted to be smart?? Nice try.");
    

    【讨论】:

      【解决方案4】:

      2 种良好保护的解决方案:

      1. 阿帕奇

      使用 htaccess

      RedirectMatch 404 "\.(sql|sh|java|class|inc\.php)$"
      

      或者在/etc/apache2/sites-enabled

      <VirtualHost *:80>
      #...
      RedirectMatch 404 "\.(sql|sh|java|class|inc\.php)$"
      #...
      </VirtualHost>
      

      然后像这样命名你的文件:myInternalFileIncludeOnly.inc.php

      1. PHP

      通过这个示例代码,PHP 能够检测包含:

      if( get_included_files()[0] == __FILE__ ){
          echo '2.php direct access';
      }else{
          echo '2.php was included';
      }
      

      编辑: 请参阅 Tim 的答案,因此,如果您有前置 include (cf php.ini),请使用:

      if(
          (!ini_get('auto_prepend_file') && get_included_files()[0] === __FILE__)
          ||
          ini_get('auto_prepend_file') && (($__FILE__=str_replace('\\','/',__FILE__)) === str_replace('\\','/',$_SERVER['DOCUMENT_ROOT'].$_SERVER['SCRIPT_FILENAME']) || $__FILE__ === str_replace('\\','/',$_SERVER['SCRIPT_FILENAME']) )
      )
          echo '2.php direct access',"\n";
      

      【讨论】:

      • 我自己总是使用 get_included_files(),但我发现它在使用 php prepend 的机器上不起作用。
      • 查看我的编辑。它适用于 Windows 吗?但是这个新版本需要更多的计算......
      【解决方案5】:

      我在一些应用程序中看到了这种模式:

      1. main,直接运行文件运行define('SOMETHING', 'SOMEVALUE');
      2. 包含的文件以if(!defined('SOMETHING')) return; 开头

      【讨论】:

        【解决方案6】:

        包含的 PHP 文件应包含类/函数/变量定义,而不是内联逻辑/输出。

        直接运行它们基本上是无操作的。

        【讨论】:

        • 这很好,但有时在上面放一些代码会很有用。我还使用include 来获取请求的页面内容,而无需使用数据库或其他复杂的东西(模板等...)
        • @Giacomo:我不明白你的用例。非封装代码是 ew,会导致与您所询问的问题一样的问题。
        • -1 因为这是使用 ajax 和本机加载页面的便捷方式。回答有点仓促。 if (my_is_included()) my_output_header().
        • @tim:这可能很“方便”,但它很讨厌、混乱和危险。您应该有一个带有控制器类的 一个 入口点,该控制器类包含它需要的其他脚本并调用其中的函数来解析输入和打印输出。不是这些随机输出头文件的无封装函数调用。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-03-06
        • 2011-01-24
        • 2014-03-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-01-09
        相关资源
        最近更新 更多