2

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

4

11 回答 11

21

我正在寻找一种方法来确定文件是否已被包含或直接调用,所有这些都来自文件中。在我的探索中的某个时刻,我通过了这个线程。从 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

我知道回答主要问题“有点”晚了,但我猜想它可能对任何与我处于相同情况并且也经过的人有用。

于 2011-08-29T02:55:51.780 回答
13

一种优雅的方法是将只能通过包含访问的所有文件放在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 管理,您可以将应用程序上的所有请求限制在此文件中,这将是一个很好的选择安全的起点。

于 2010-12-28T12:14:31.137 回答
6

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

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

// in each module
if (!defined("LEGIT_REQUEST")) 
  die ("This module cannot be called directly.");
于 2010-12-28T12:07:15.317 回答
5

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

#
# Private directory
#
Order allow,deny
Deny from all
于 2010-12-28T12:17:09.527 回答
4

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

define('TEST', true);

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

if (!defined('TEST')) {
    die('Do not cheat.');
}
于 2010-12-28T12:09:54.830 回答
3

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

于 2010-12-28T12:15:09.043 回答
1

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

于 2010-12-28T12:09:42.910 回答
1

全局.php

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

模块.php

define("in_myscript", 1);
include("global.php");
于 2010-12-28T12:10:37.190 回答
1

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

<?php $inc = get_included_files(); if(basename(__FILE__) == basename($inc[0])) exit();
于 2014-03-22T08:01:15.637 回答
0

作为习惯,我构建了一个控制台类,用于使用 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, '');
  }
}
于 2010-12-28T15:04:27.890 回答
0

简短而简单(用于 CLI):

if (__FILE__ == realpath($argv[0]))
    main();
于 2015-01-20T01:25:44.820 回答