PHP:检查文件是不是直接加载而不是包含?
Posted
技术标签:
【中文标题】PHP:检查文件是不是直接加载而不是包含?【英文标题】:PHP: Check if a file is loaded directly instead of including?PHP:检查文件是否直接加载而不是包含? 【发布时间】:2011-01-24 17:12:08 【问题描述】:有没有办法阻止用户查看文件但仍将其作为包含在php 中的另一个文件中使用?
【问题讨论】:
【参考方案1】:如果你使用
define('APP_RAN');
在包含它的文件中,然后放入
if(!defined('APP_RAN')) die();
或者
defined('APP_RAN') or die();
(更容易阅读)
在包含的文件中,如果你直接访问它们会死掉。
不过,最好将所有包含的文件放在 DocumentRoot 之上。
例如,如果您的索引页位于
/my/server/domain/public_html
你应该把包含的文件放进去
/my/server/domain/
【讨论】:
这是最好的解决方案,它是基本网站保护的一部分。 哪一个,定义还是路径? 两者,即使你只能使用一个。路径更容易实现,定义需要一些时间来实现。defined('APP_RAN') or die();
- 同意。样板文件应尽可能短。【参考方案2】:
我的建议:
<?php
if (__FILE__ == $_SERVER['SCRIPT_FILENAME'])
header($_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found');
exit("<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n<html><head>\r\n<title>404 Not Found</title>\r\n</head><body>\r\n<h1>Not Found</h1>\r\n<p>The requested URL " . $_SERVER['SCRIPT_NAME'] . " was not found on this server.</p>\r\n</body></html>");
else
// your code
?>
1.) 检查是否直接调用,否则会抛出错误
2.) 它输出一个 404 标准 apache 错误页面(请与您的原始 404 页面进行比较或简单地包含该页面)以通过默默无闻来增加安全性
3.) else 部分避免在文件上传到实时环境时部分执行(PHP 不等待“?>”)。如果您包含的文件仅包含一个函数/一个类,则不需要它。
【讨论】:
为了支持在 Linux 和 Windows 上托管的 PHP,我用 str_replace('\\', '/', _ _ FILE _ _) 包装了 _ _ FILE _ _ 有用的是它不依赖于外部变量。【参考方案3】:不要在文件中使用任何全局代码,只使用函数和方法。那么就不需要关心包含与直接使用。
【讨论】:
同意;如果有人设法猜出您的仅包含函数和类的包含文件的名称,那么他们在尝试查看它时所看到的只是一个空白页。如果您使用包含输出 html(我不喜欢这样做),您可以将其放入函数中,包含文件并调用该函数。 这是一个很好的解决方案,但要注意:如果您将脚本留在公共目录中,请记住 a) 确保没有任何语法错误或 b) 关闭显示错误,因为否则它们仍然会出现,即使文件只包含函数/类。 我觉得应该指出该技术不仅仅用于阻止直接访问。使用“需要外部变量”技术,您可以在直接访问文件时赋予文件不同的功能。因此,例如,您可以轻松地从 post 中提取变量并运行主函数,而不仅仅是等待函数被调用并打印响应,从而允许您将其变成一个简单的 REST 接口。【参考方案4】:if (!defined('FLAG_FROM_A_PARENT'))
// Works in all scenarios but I personally dislike this
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 break on Windows due to mixed DIRECTORY_SEPARATOR
if (basename(__FILE__) == basename($_SERVER['SCRIPT_FILENAME']))
// Doesn't work with files with the same basename but different paths
if (realpath(__FILE__) == realpath($_SERVER['DOCUMENT_ROOT'].$_SERVER['SCRIPT_NAME']))
// Seems to do the trick
【讨论】:
【参考方案5】:只需将文件存储在您的网络根目录之外。
【讨论】:
并非如此。根据经验,某些框架中的某些模块坚持将您的附加代码放置在特定位置,例如 Laravel。 @liljoshu 是的,像 Laravel 这样的框架会存储你的大部分文件......猜猜在 web 根目录之外的位置。 @Erik 昨天 如果您有幸在设计得当的网站上工作,或者您有幸自己设计网站,但情况并非总是如此,那么可能就是这种情况。遗憾的是,我使用的最后一个 Laravel 除了 web root 之外什么都没有。即便如此,并非所有文件都在 Web 根目录之外。有时您需要在 Web 根目录下工作,或者因为情况迫使它,或者这就是框架的功能。这绝对是 a 好的答案。只是不是唯一的好的答案。 Frameworks 对我放置代码的位置没有任何发言权。应避免任何妨碍基本安全措施的事情。【参考方案6】:if (__FILE__ == $_SERVER['DOCUMENT_ROOT'].$_SERVER['PHP_SELF'])
die("Direct access forbidden");
无论它在哪里,它都可以工作。 (感谢@col-sharpnel 指出了一个不完整但很好的解决方案。)
【讨论】:
-1 不要依赖 PHP_SELF,因为这是 site.com/myfile.php/badstring.php 的安全漏洞 我不明白这是一个漏洞。在简单的安装中,site.com/myfile.php/badstring.php 只会导致 404 错误。 php_self 由代理根据请求 URI 设置。 /index.php/dummy_hijack.php 不会解析 /index.php。 $_SERVER['DOCUMENT_ROOT'].$_SERVER['SCRIPT_NAME'] 可以。您的示例在某种程度上是安全的,但并不准确。但是,使用 basename 的用户在使用 PHP_SELF 时会遇到麻烦。 if (basename(FILE) == basename($_SERVER['PHP_SELF'])) 好的。我现在明白了。随时编辑我的帖子以添加警告。不知道自己能不能解释清楚。 当程序可以被包含或直接访问并且您需要一种方法来检测正在使用的方法时,这种解决方案似乎很好。【参考方案7】:有get_included_files 函数。你可以这样使用它:
if ( empty(get_included_files()) ) die("Direct access forbidden");
【讨论】:
从 PHP5 开始应该注意这个条件永远不会通过。见this comment in the docs。当前文件始终在数组中,因此数组始终至少有 1 项,因此永远不会为空。在 PHP5+ 中,可以尝试if ( count(get_included_files()) === 1 ) die("Direct access forbidden");
。【参考方案8】:
在 Apache 下,这很简单:在 .htaccess 中添加 <Files>
指令,以防止从 Web 浏览器访问它。这不适用于 PHP includes
,最好隐藏多个文件以防止浏览器访问(尽管通常您会尝试将所有不可访问的文件放在一个目录中)。
<Files="myprivatefile.php">
deny from all
</Files>
在不同的网络服务器下,您可以将文件隐藏在文档根目录之外,但在某些情况下(例如,如果您的脚本严格使用 open_basedir
'd),它将不起作用。
【讨论】:
在大多数使用 LAMP 堆栈的商业环境中,这是更“标准”的方式(这是您在商业世界中看到 PHP 最多的地方。)【参考方案9】:检查有多少包含的文件...
if(count(get_required_files()) < 2) die();
或者应该有多少个最小值而不是 2 个
【讨论】:
This seems pretty foolproof, wow.(只要你记得做就行) 看起来不错,只要你放的文件不需要文件(或者你把上面的检查放在需要文件的地方。)【参考方案10】:如果您想阻止它在未包含时显示,这里有一种更自动化的方法。
if (basename(__FILE__) == basename($_SERVER['PHP_SELF'])) header("HTTP/1.0 404 Not Found");
这样,如果您最终更改文件名或其他内容,它仍然可以工作。
【讨论】:
如果包含的文件名与全局文件名相同,这将不起作用。【参考方案11】:if($argv[0] == basename(__FILE__))
include_once('/path/to/file.php');
【讨论】:
【参考方案12】:这个帖子中有很多很好的答案 - 这里有一个尚未提及。
您可以在扩展名中使用i
命名包含的 PHP 文件,例如someincludedfile.phpi
然后配置 Apache 使其不提供 phpi
文件。瞧。
缺点:
(!) 高度依赖于 Apache 配置(因此,如果有人忽略该行,您很容易受到攻击)——在这种情况下,浏览器可能会以纯文本形式看到整个 PHP 脚本,因为它不会传递给 PHP(真的很糟糕!) 重命名包含的文件可能很烦人优点:
明确要包含哪些文件,哪些不在您的代码中 您不必移动文件层次结构就个人而言,我宁愿只是将文件移动到文档根目录之外的目录中,但如果您有一个大型遗留应用程序,这可能会更快地更改。
链接:http://www.ducea.com/2006/07/21/apache-tips-tricks-deny-access-to-certain-file-types/
【讨论】:
【参考方案13】:我会选择Chacha102's solution。
此外,正如您的问题标题所说的“如何检查”,您也可以这样做而无需使用
定义变量// secret.php is the name of this file.
if($_SERVER["SCRIPT_FILENAME"]=='secret.php') die();
【讨论】:
这不起作用,因为 SCRIPT_FILENAME 包含完整路径,而不仅仅是文件名。【参考方案14】:只是用$_SERVER
变量扩展解决方案 - 下面是一个小的 .php 文件,在 cmets 中是我在 Ubuntu 上进行的bash
测试的副本;关键是,这些变量可能会根据访问类型以及是否使用符号链接而发生很大变化(还要注意,在下面的代码中,我必须在 echo 语句中转义“?\>
”,否则语法颜色会中断;如果尝试代码,请删除反斜杠):
<?php
function report($label, $value)
printf ("%23s: %s\n", $label, $value);
report("DOCUMENT_ROOT.PHP_SELF", $_SERVER['DOCUMENT_ROOT'].$_SERVER['PHP_SELF'] );
report("SCRIPT_FILENAME", $_SERVER['SCRIPT_FILENAME'] );
report("__FILE__", __FILE__ );
report("PHP_SAPI", PHP_SAPI );
/*
# the test (bash):
home~$ mkdir /tmp/ptest
home~$ cd /tmp/ptest/
ptest$ ln -s /real/path/to/varcheck.php .
ptest$ echo '<? require_once "varcheck.php"; ?\>' > varcheckincl.php
# ... and in a separate terminal, run
# php (>5.4) cli server at same (/tmp/ptest) location:
ptest$ php-5.4.10 -S localhost:8000
# back to first terminal, the test - and output:
ptest$ php varcheck.php
DOCUMENT_ROOT.PHP_SELF: varcheck.php
SCRIPT_FILENAME: varcheck.php
__FILE__: /real/path/to/varcheck.php
PHP_SAPI: cli
ptest$ php -r 'require_once "varcheck.php";'
DOCUMENT_ROOT.PHP_SELF: -
SCRIPT_FILENAME:
__FILE__: /real/path/to/varcheck.php
PHP_SAPI: cli
ptest$ php varcheckincl.php
DOCUMENT_ROOT.PHP_SELF: varcheckincl.php
SCRIPT_FILENAME: varcheckincl.php
__FILE__: /real/path/to/varcheck.php
PHP_SAPI: cli
ptest$ wget http://localhost:8000/varcheck.php -q -O -
DOCUMENT_ROOT.PHP_SELF: /tmp/ptest/varcheck.php
SCRIPT_FILENAME: /tmp/ptest/varcheck.php
__FILE__: /real/path/to/varcheck.php
PHP_SAPI: cli-server
ptest$ wget http://localhost:8000/varcheckincl.php -q -O -
DOCUMENT_ROOT.PHP_SELF: /tmp/ptest/varcheckincl.php
SCRIPT_FILENAME: /tmp/ptest/varcheckincl.php
__FILE__: /real/path/to/varcheck.php
PHP_SAPI: cli-server
*/
?>
【讨论】:
以上是关于PHP:检查文件是不是直接加载而不是包含?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 google.load 加载 javascript 文件,而不是直接使用 freemarker 模板语言中的标签?