我的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']
陷阱,但我想知道是否有某种优雅的方式来做这件事。
答案 0 :(得分:17)
我一直在寻找一种方法来确定文件是否已被直接包含或调用,所有这些都来自文件。在我的任务的某个时刻,我通过了这个线程。检查PHP手册中的这个和其他网站和页面上的各种其他线程我得到了启发并提出了这段代码:
if ( basename(__FILE__) == basename($_SERVER["SCRIPT_FILENAME"]) ) {
echo "called directly";
}
else {
echo "included/required"
}
本质上,它会比较当前文件的名称(可以包含的名称)是否与正在执行的文件相同。
__ FILE__是一个PHP魔术常量,它存储文件的完整路径和文件名,它的美妙之处在于,如果文件已被包含或需要,它仍会返回此文件的完整路径和文件名(包含文件) (Magic Constants Manual:http://php.net/manual/en/language.constants.predefined.php)
$ _ SERVER [“SCRIPT_FILENAME”]返回当前正在执行的脚本的绝对路径名。当包含/需要文件时,它不会被执行(只是包含)它返回(让我们说的)“父”文件的路径名(包含另一个文件的那个文件和被执行的文件)。
basename(string $ path)是一个返回path的尾随名称组件的函数,在本例中是文件名。你也可以只比较完整的路径和文件名,这确实会更好,使用这个功能并不是真的很有用,但这种方式感觉更干净,jajaj。
(basename():http://php.net/manual/en/function.basename.php)
我知道回答主要问题已经“迟到”了,但我猜想对于那些与我情况相同并且也经过的人来说,这可能是有用的。
答案 1 :(得分:13)
一种优雅的方法是放置所有只能通过在网络目录外访问的文件。
假设您的网站目录是/ 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配置,以允许您的include目录)。
你的web目录中真正需要的唯一文件是我们称之为bootstrap(index.php),有一个很好的重写规则或一个很好的url管理,你可以将你对应用程序的所有请求限制在这个文件中,这样就可以了是一个很好的安全起点。
答案 2 :(得分:6)
确保模块不直接调用的一种流行方法是在主脚本中定义一个常量,并在模块中检查该常量。
// index.php
define("LEGIT_REQUEST", true);
// in each module
if (!defined("LEGIT_REQUEST"))
die ("This module cannot be called directly.");
答案 3 :(得分:5)
为了完整起见,另一种可能性是将这些文件移动到不公开的目录。但是,托管服务提供商使用的某些控制面板使这变得不可能在这种情况下,如果您使用的是Apache,则可以在目录中放置.htaccess
文件:
#
# Private directory
#
Order allow,deny
Deny from all
答案 4 :(得分:4)
一种常见的技术是将其添加到主模块(包含之前)
define('TEST', true);
并在每个子模块的第一行添加类似的内容
if (!defined('TEST')) {
die('Do not cheat.');
}
答案 5 :(得分:3)
定义常量并检查它的另一种方法是简单地将index.php包含的文件放在文档根区域之外。这样,用户就无法通过您的Web服务器直接访问它们。这显然也是最安全的方式,以防您的Web服务器将来出现配置错误,例如。将PHP文件显示为纯文本。
答案 6 :(得分:1)
您可以在clientes.php中define('SOMETHING', null)
,然后在模块中查看if (!defined('SOMETHING')) die;
。
答案 7 :(得分:1)
global.php
if(!defined("in_myscript"))
{
die("Direct access forbidden.");
}
module.php
define("in_myscript", 1);
include("global.php");
答案 8 :(得分:1)
无需定义常量或使用htaccess或使用特定目录结构或依赖理论上可以修改的$ _SERVER数组的通用方法是使用此方法启动每个仅包含(无直接访问)文件代码:
<?php $inc = get_included_files(); if(basename(__FILE__) == basename($inc[0])) exit();
答案 9 :(得分:0)
作为习惯的习惯,我有一个控制台类,用于向FirePHP控制台发送消息,错误等。在Console类的write()方法中,我检查一下$ _REQUEST [debug] == 1,这样我就不会在生产中弹出一些东西时向用户暴露错误,他们必须知道请求是什么变量是访问调试信息。
在我添加的每个文件的顶部:
Console::debug('fileName.php is loaded.');
这是一个来自它的snippit给你正确的想法:
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, '');
}
}
答案 10 :(得分:0)
简短(对于CLI):
if (__FILE__ == realpath($argv[0]))
main();