如何知道php脚本是否通过require_once()被调用?

发布于 2024-10-09 07:06:57 字数 983 浏览 0 评论 0原文

我的网络应用程序有很多模块。每个模块都有一个“主”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'] 陷阱,但我想知道是否存在某种一种优雅的方式来做到这一点。

My webapp has a buch of modules. Each module has a 'main' php script which loads submodules based on a query sent to the main module:

//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>";
} 

This main module deals with security and imports many resources all submodules need. The big problem shows up when a user asks for any of the submodules, bypassing all the security measures on the main module! My idea was to add a line on every submodule to test if it was being called directly and deny access or if its been called via another script, and continue. The least thing I would like to do is redo the security checking on every single file, since it does a bunch of query's to the database.

Does a php script know if its been called via a require_once() or a direct call ? I've been trying to implement some sort of $_SERVER['REQUEST_URI'] and $_SERVER['PHP_SELF'] pitfall but I was wondering if there was some sort of an elegant way of doing this.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

酷炫老祖宗 2024-10-16 07:06:57

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

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

else {
  echo "included/required"
}

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


说明:

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

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

  • basename(string $path) 是一个返回路径的尾随名称部分的函数,在本例中是文件名。您也可以只比较完整路径和文件名,这确实会更好,实际上没有必要使用此功能,但这样感觉更干净,jajaj。
    (basename(): http://php.net/manual/en/function.basename.php )

我知道回答主要问题有点晚了,但我猜这对那些与我处境相同并且也经过的人来说可能会有用。

I was looking for a way to determine if a file have been included or called directly, all from within the file. At some point in my quest I passed through this thread. Checking various other threads on this and other sites and pages from the PHP manual I got enlightened and came up with this piece of code:

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

else {
  echo "included/required"
}

In essence it compares if the name of the current file (the one that could be included) is the same as the file that is beeing executed.


EXPLANATION:

  • __FILE__ is a PHP magic constant that stores the full path and filename of the file, the beauty of it is that if the file has been included or required it still returns the full path and filename of such file (the included file).
    (Magic Constants Manual: http://php.net/manual/en/language.constants.predefined.php)

  • $_SERVER["SCRIPT_FILENAME"] returns the absolute pathname of the currently executing script. As when a file is included/required it's not executed (just included) it returns the path name of the (let's say) "parent" file (the one that includs the other file and the one that gets executed).

  • basename(string $path) is a function that returns the trailing name component of path, that in this case is the file name. You could also just compare the full path and filename, that would be indeed better, it isn't really neceseary to use this function but it feels cleaner this way, jajaj.
    (basename(): http://php.net/manual/en/function.basename.php)

I know it's a "bit" late to be answering the main question but I guessed that it could be useful to anyone who's on the same situation that I was and that also passes by.

囍笑 2024-10-16 07:06:57

一种优雅的方法是将只能通过 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); 

然后没有人能够直接访问您的库。

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

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

One elegant way is putting all your files which should only be accessed via include outside the web directory.

Say your web directory is /foo/www/, make an include directory /foo/includes and set this in your 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); 

Then nobody will be able to access your libraries directly.

There's a lot of other things you could do (test a global variable is set, use only classes in libraries, etc) but this one is the most secure one. Every file which is not in your DocumentRoot cannot be accessed via an url,. But that does not mean PHP cannot get access to this file (check as well your open_basedir configuration if you have it not empty, to allow your include dir in it).

The only file you really need in your web directory is what we call the bootstrap (index.php), with a nice rewrite rule or a nice url managment you can limit all your requests on the application to this file, this will be a good starting point for security.

寄人书 2024-10-16 07:06:57

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

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

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

One popular method to make sure modules are not called directly is defining a constant in the main script, and checking for that constant in the module.

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

// in each module
if (!defined("LEGIT_REQUEST")) 
  die ("This module cannot be called directly.");
杯别 2024-10-16 07:06:57

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

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

For the sake of completeness, the other possibility is to move such files to a directory that's not publicly available. However, some control panels used by hosting providers make this impossible. In such case, if you are using Apache you can place an .htaccess file inside the directory:

#
# Private directory
#
Order allow,deny
Deny from all
可遇━不可求 2024-10-16 07:06:57

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

define('TEST', true);

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

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

A common technique is to add this to the main module (before the includes)

define('TEST', true);

and to add something like that at the first line of every submodule

if (!defined('TEST')) {
    die('Do not cheat.');
}
红焚 2024-10-16 07:06:57

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

An alternative to defining a constant and checking it is to simply put the files that index.php includes outside of the document root area. That way the user can't directly access them via your web server at all. This is also obviously the most secure way, in case your web server has a configuration error in future that eg. displays PHP files as plain text.

尹雨沫 2024-10-16 07:06:57

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

You can define('SOMETHING', null) in clientes.php and then check if (!defined('SOMETHING')) die; in the modules.

若沐 2024-10-16 07:06:57

全局.php

if(!defined("in_myscript"))
{
    die("Direct access forbidden.");
}

模块.php

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

global.php

if(!defined("in_myscript"))
{
    die("Direct access forbidden.");
}

module.php

define("in_myscript", 1);
include("global.php");
凉宸 2024-10-16 07:06:57

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

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

A generic way that works without having to define a constant or use htaccess or use a specific directory structure or depend on the $_SERVER array that could theoretically be modified is to start each include-only (no direct access) file with this code:

<?php $inc = get_included_files(); if(basename(__FILE__) == basename($inc[0])) exit();
杀手六號 2024-10-16 07:06:57

作为习惯实践,我构建了一个控制台类,用于使用 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, '');
  }
}

As practice of habit I have a console class built to send messages, errors, etc. to console with FirePHP. Inside the Console class write() method I have a check to see if a $_REQUEST[debug] == 1, that way I'm not exposing errors to users if something pops up on production and they would have to know what the request variable is to access the debug information.

At the top of every file I add:

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

here is a snippit from it to give you the right idea:

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, '');
  }
}
丘比特射中我 2024-10-16 07:06:57

简短(对于 CLI):

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

Short and simple (for CLI):

if (__FILE__ == realpath($argv[0]))
    main();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文