如何知道是不是通过 require_once() 调用了 php 脚本? [复制]

Posted

技术标签:

【中文标题】如何知道是不是通过 require_once() 调用了 php 脚本? [复制]【英文标题】:How to know if php script is called via require_once()? [duplicate]如何知道是否通过 require_once() 调用了 php 脚本? [复制] 【发布时间】:2011-05-31 13:26:26 【问题描述】:

我的 webapp 有很多模块。每个模块都有一个 'main' php 脚本,它根据发送到主模块的查询加载子模块:

//file: clientes.php

//check for valid user...
//import CSS and JS...

switch( $_GET["action"] )

    case "lista" :          require_once("clientes.lista.php"); break;
    case "listaDeudores" :  require_once("clientes.listaDeudores.php"); break;
    case "nuevo" :          require_once("clientes.nuevo.php"); break;
    case "detalles" :       require_once("clientes.detalles.php"); break;
    case "editar" :         require_once("clientes.editar.php"); break;         
    default : echo "<h1>Error</h1><p>El sitio ha encontrado un error.</p>";
 

这个主模块处理安全性并导入所有子模块所需的许多资源。当用户请求任何子模块时,就会出现大问题,绕过主模块上的所有安全措施!我的想法是在每个子模块上添加一行,以测试它是否被直接调用并拒绝访问,或者是否通过另一个脚本调用它,然后继续。我最不想做的就是对每个文件重新进行安全检查,因为它会对数据库进行大量查询。

php 脚本是否知道它是通过require_once() 调用还是直接调用?我一直在尝试实现某种$_SERVER['REQUEST_URI']$_SERVER['PHP_SELF'] 陷阱,但我想知道是否有某种优雅的方式来做到这一点。

【问题讨论】:

脚本通过 require_once 包含,但未执行。 这能回答你的问题吗? Check if a file was included or loaded 【参考方案1】:

简短(用于 CLI):

if (__FILE__ == realpath($argv[0]))
    main();

【讨论】:

【参考方案2】:

一种无需定义常量或使用 htaccess 或使用特定目录结构或依赖理论上可以修改的 $_SERVER 数组即可工作的通用方法是使用此启动每个仅包含(无直接访问)文件代码:

<?php $inc = get_included_files(); if(basename(__FILE__) == basename($inc[0])) exit();

【讨论】:

【参考方案3】:

一种优雅的方法是将只能通过 include 访问的所有文件放在 Web 目录之外

假设你的 web 目录是 /foo/www/,创建一个包含目录 /foo/includes 并在你的 include_path 中设置它:

$root = '/foo';
$webroot = $root.'/www'; // in case you need it on day
$lib = $root.'/includes';
// this add your library at the end of the current include_path
set_include_path(get_include_path() . PATH_SEPARATOR . $lib); 

那么没有人将能够直接访问您的库。

您还可以做很多其他事情(测试设置的全局变量、仅使用库中的类等),但这是最安全的事情。不能通过 url 访问不在 DocumentRoot 中的每个文件。但这并不意味着 PHP 无法访问此文件(如果您的 open_basedir 配置不为空,请检查您的配置,以允许您在其中包含您的目录)。

你真正需要在你的 web 目录中的唯一文件是我们所说的引导程序 (index.php),通过一个很好的重写规则或一个很好的 url 管理,你可以限制你对应用程序的所有请求到这个文件,这将成为安全的良好起点。

【讨论】:

如果可能且可行,这是最好的方法。 +1 以前从未这样做过,很惊讶我从未想过。谢谢你教老狗新把戏。 @OldCode101:没问题,这是我们在 PHP 项目中很少看到的,因为它们经常被部署在 DocumentRoot 中直接使用 FTP 访问。 在声明之后使用$foo 还应包括$ 变量前缀。【参考方案4】:

我正在寻找一种方法来确定文件是否已被包含或直接调用,所有这些都来自文件中。在我的探索中的某个时刻,我通过了这个线程。从 PHP 手册中检查此站点和其他站点和页面上的各种其他线程,我得到启发并想出了这段代码:

if ( basename(__FILE__) == basename($_SERVER["SCRIPT_FILENAME"]) ) 
  echo "called directly";


else 
  echo "included/required"

本质上,它比较当前文件的名称(可以包含的文件)是否与正在执行的文件相同。


解释:

__FILE__ 是一个 PHP 魔术常量,用于存储文件的完整路径和文件名,它的美妙之处在于,如果文件已被包含或需要,它仍然返回该文件的完整路径和文件名(包含的文件)。 (魔常数手册:http://php.net/manual/en/language.constants.predefined.php)

$_SERVER["SCRIPT_FILENAME"] 返回当前执行脚本的绝对路径名。当包含/需要一个文件时,它不会执行(只是包含)它返回(比如说)“父”文件(包含另一个文件和被执行的文件)的路径名。

basename(string $path) 是一个函数,它返回路径的尾随名称组件,在这种情况下是文件名。您也可以只比较完整路径和文件名,这确实会更好,使用此功能并不是真的需要,但这样感觉更干净,jajaj。 (basename():http://php.net/manual/en/function.basename.php)

我知道现在回答主要问题“有点”晚了,但我猜想这对任何与我处于相同情况且路过的人都有用。

【讨论】:

这很好。你不需要定义你需要这个文件的地方 - 麻烦,如果你忘记了 - 灾难。此外,我们还可以测试 basename($argv[0]) 以覆盖命令行调用 到目前为止,这对我来说是最有吸引力的DRY 实现。谢谢! 你不应该使用 basename,许多文件具有相同的名称但驻留在不同的目录中,否则这是一个很好的解决方案,与我到达这里时或多或少相同gist.github.com/jthoburn/5302060 但是调用脚本可以在包含之前设置这些值,不是吗?例如$_SERVER['SCRIPT_NAME'] = 'the_included_script.php'; 非常感谢您的回答!这真的让我朝着我需要的方向前进。我可以使用它来包含我的课程,以便能够将课程与其他语言一起使用,并在需要时吐出 JSON。这是我刚刚使用 PHP 电子邮件验证脚本 github.com/DukeOfMarshall/PHP---JSON-Email-Verification/tree/… 所做的一个示例【参考方案5】:

作为习惯,我构建了一个控制台类,用于使用 FirePHP 向控制台发送消息、错误等。在 Console 类的 write() 方法中,我检查了 $_REQUEST[debug] == 1,这样我就不会向用户暴露错误,如果在生产中弹出某些东西并且他们必须知道请求是什么变量是访问调试信息。

在我添加的每个文件的顶部:

Console::debug('fileName.php is loaded.');

这里有一个片段可以给你正确的想法:

class Console

  public static function write($msg,$msg_type='info',$msg_label='')
    if(isset($_REQUEST['debug']) && $_REQUEST['debug'] == 'PANCAKE!')
      ob_start();
      switch($msg_type)
        case 'info':
          FB::info($msg, $msg_label);
          break;
        case 'debug':
          FB::info($msg, 'DEBUG')
          break;
          ...
      
    
  

  public static function debug($msg)
    Console::write($msg, '');
  

【讨论】:

【参考方案6】:

确保不直接调用模块的一种流行方法是在主脚本中定义一个常量,并在模块中检查该常量。

// index.php
define("LEGIT_REQUEST", true);

// in each module
if (!defined("LEGIT_REQUEST")) 
  die ("This module cannot be called directly.");

【讨论】:

对额外检查感到好奇。哪些情况下!defined("LEGIT_REQUEST")不够用? @Álvaro 是的,我写的时候也是这么想的,这真的是多余的——不可能将常量设置为false。已移除 还有更多面向未来的方法可以实现这一点:gist.github.com/jthoburn/5302060【参考方案7】:

为了完整起见,另一种可能性是将此类文件移动到不公开的目录中。但是,托管服务提供商使用的一些控制面板使这成为不可能。在这种情况下,如果您使用的是 Apache,您可以在目录中放置一个 .htaccess 文件:

#
# Private directory
#
Order allow,deny
Deny from all

【讨论】:

【参考方案8】:

定义一个常量并检查它的另一种方法是简单地将 index.php 包含的文件放在文档根区域之外。这样,用户根本无法通过您的 Web 服务器直接访问它们。这显然也是最安全的方式,以防万一您的 Web 服务器将来出现配置错误,例如。将 PHP 文件显示为纯文本。

【讨论】:

【参考方案9】:

global.php

if(!defined("in_myscript"))

    die("Direct access forbidden.");

模块.php

define("in_myscript", 1);
include("global.php");

【讨论】:

【参考方案10】:

一种常见的技术是将其添加到主模块(在包含之前)

define('TEST', true);

并在每个子模块的第一行添加类似的内容

if (!defined('TEST')) 
    die('Do not cheat.');

【讨论】:

这是最有效的方式,因为通常所有包含都是从一个中心节点调用的,基于一些变量或条件。这是服务器解析的最少代码,所以用这个!【参考方案11】:

您可以在clientes.php 中define('SOMETHING', null),然后在模块中查看if (!defined('SOMETHING')) die;

【讨论】:

以上是关于如何知道是不是通过 require_once() 调用了 php 脚本? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

是否有 require_once 的 HTML 变体?

如何让 require_once 从上面的目录中获取文件?

PHP 是不是需要、require_once 或包含在页面加载时自动读取文件?

无法让本地主机上的 ajax 调用 require_once 工作

require_once 返回 true 而不是预期的对象

警告:require_once():在 PHP 中依赖系统的时区设置错误是不安全的