防止直接访问 php 包含文件

我有一个 php 文件,我将使用作为独家作为一个包括。因此,我想抛出一个错误,而不是在通过输入 URL 而不是包含 URL 直接访问它时执行它。

基本上,我需要在 php 文件中执行以下检查:

if ( $REQUEST_URL == $URL_OF_CURRENT_PAGE ) die ("Direct access not premitted");

有什么简单的方法吗?

265351 次浏览

最简单的方法是在调用 include 的文件中设置一些变量,例如

$including = true;

然后在包含的文件中检查变量

if (!$including) exit("direct access not permitted");

防止直接访问文件的最佳方法是将它们放置在 Web 服务器文档根目录之外(通常是在上一级)。您仍然可以包含它们,但是不可能有人通过 http 请求访问它们。

我通常一直这样做,并将所有 PHP 文件放在文档根目录之外,除了 引导程序文件引导程序文件-文档根目录中的一个 index.PHP,它开始路由整个网站/应用程序。

对于通用的“ PHP 应用程序运行在 Apache 服务器上,您可能完全控制,也可能不完全控制”的情况,最简单的方法是将您的包含放在一个目录中,并拒绝访问您的。Htaccess 文件。为了避免人们使用 Google 的麻烦,如果你正在使用 Apache,把它放在一个名为“。在你不想访问的目录下:

Deny from all

如果你真的完全控制了服务器(现在即使是小应用程序也比我第一次写这个答案时更常见) ,最好的方法是把你想保护的文件放在你的网络服务器服务的目录之外。因此,如果你的应用程序是在 /srv/YourApp/,设置服务器从 /srv/YourApp/app/服务文件,并把包括在 /srv/YourApp/includes,所以从字面上没有任何 URL,可以访问它们。

将此内容添加到只希望包含的页面

<?php
if(!defined('MyConst')) {
die('Direct access not permitted');
}
?>

然后在包含它的页面上添加

<?php
define('MyConst', TRUE);
?>

比如:

<?php
if ($_SERVER['SCRIPT_FILENAME'] == '<path to php include file>') {
header('HTTP/1.0 403 Forbidden');
exit('Forbidden');
}
?>

Chuck 解决方案的一个替代方案(或补充方案)是拒绝访问匹配特定模式的文件,方法是在。Htaccess 文件

<FilesMatch "\.(inc)$">
Order deny,allow
Deny from all
</FilesMatch>

事实上,我的建议是做所有这些最佳实践。

  • 将文档放在 webroot OR 之外的一个被 webserver 拒绝访问的目录中 还有
  • 使用隐藏文档检查的可见文档中的定义:
      if (!defined(INCL_FILE_FOO)) {
header('HTTP/1.0 403 Forbidden');
exit;
}

这样,如果文件以某种方式放错了位置(一个错误的 ftp 操作) ,它们仍然受到保护。

我有一个文件,我需要采取不同的行动,当它包括时,与当它被直接访问(主要是 print()return())这里有一些修改的代码:

if(count(get_included_files()) ==1) exit("Direct access not permitted.");

被访问的文件始终是一个包含的文件,因此 = = 1

除了。我已经在各种框架中看到了一个有用的模式,例如 ruby on ails。它们在应用程序根目录中有一个单独的 pub/目录,库目录作为 pub/存在于 同样的水平的目录中。类似这样的事情(不是很理想,但你明白我的意思) :

app/
|
+--pub/
|
+--lib/
|
+--conf/
|
+--models/
|
+--views/
|
+--controllers/

将 Web 服务器设置为使用 pub/作为文档根目录。这为您的脚本提供了更好的保护: 虽然它们可以从文档根目录加载必要的组件,但是不可能从因特网访问这些组件。除了安全性之外,另一个好处是所有东西都在一个地方。

这种设置比仅仅在每个包含的文件中创建检查要好,因为“访问不允许”消息是攻击者的线索,而且比。Htaccess 配置,因为它不是基于白名单的: 如果您搞砸了文件扩展名,它将不会在 lib/、 conf/etc 目录中可见。

我曾经遇到过这个问题,解决方法是:

if (strpos($_SERVER['REQUEST_URI'], basename(__FILE__)) !== false) ...

但是理想的解决方案是将文件放在 web-server 文档根目录之外,正如另一个答案中提到的。

<?php
if (eregi("YOUR_INCLUDED_PHP_FILE_NAME", $_SERVER['PHP_SELF'])) {
die("<h4>You don't have right permission to access this file directly.</h4>");
}
?>

将上面的代码放在包含的 php 文件的顶部。

例如:

<?php
if (eregi("some_functions.php", $_SERVER['PHP_SELF'])) {
die("<h4>You don't have right permission to access this file directly.</h4>");
}


// do something
?>

什么 Joomla!Does 是在根文件中定义一个 Constant,并检查在包含的文件中是否定义了相同的常量。

defined('_JEXEC') or die('Restricted access');

否则

像 CodeIgniter 这样的大多数框架建议的那样,通过将所有文件放在 webroot 目录之外,可以将所有文件保持在 http 请求无法访问的范围之外。

或者甚至通过在 include 文件夹中放置一个. htaccess 文件并编写规则,可以防止直接访问。

在 Flatnux CMS (http://flatnux.altervista.org)中使用以下代码:

if ( strpos(strtolower($_SERVER['SCRIPT_NAME']),strtolower(basename(__FILE__))) )
{
header("Location: ../../index.php");
die("...");
}

我找到了这个只有 php 和不变的解决方案,它同时适用于 http 和 cli:

定义一个函数:

function forbidDirectAccess($file) {
$self = getcwd()."/".trim($_SERVER["PHP_SELF"], "/");
(substr_compare($file, $self, -strlen($self)) != 0) or die('Restricted access');
}

调用要阻止直接访问的文件中的函数:

forbidDirectAccess(__FILE__);

上面给出的这个问题的大多数解决方案都不能在 Cli 模式下工作。

您可以使用下面的方法,虽然它有一个缺陷,因为它可以伪造,除非您可以添加另一行代码,以确保请求只来自您的服务器,无论是使用 Javascript。 您可以将此代码放置在 HTML 代码的 Body 部分中,这样就会显示错误。

<?
if(!isset($_SERVER['HTTP_REQUEST'])) { include ('error_file.php'); }
else { ?>

将您的其他 HTML 代码放在这里

<? } ?>

像这样结束它,所以错误的输出将始终显示在 body 部分中,如果这是您想要的结果。

您也可以尝试重命名您不希望人们能够访问的文档。例如,您可以将其重命名为47d8498d3w.php。只要编造一些人们很可能不会作为 http 请求输入的东西。如果使用 SSI 或 PHP 包含文件,用户无论如何都不能看到文档的名称。

debug_backtrace() || die ("Direct access not permitted");

出于安全原因,我建议不要使用 $_SERVER
您可以在包含另一个变量的第一个文件中使用类似 $root=true;的变量。
并在包含的第二个文件开头使用 isset($root)

您还可以做的是密码保护目录,并保持所有的 php 脚本在那里,当然除了 index.php 文件,因为在包含密码时将不是必需的,因为它将只用于 http 访问。它还将为您提供访问脚本的选项,以备不时之需,因为您将拥有访问该目录的密码。你需要安装。目录的 htaccess 文件和一个。Htpasswd 文件对用户进行身份验证。

你也可以使用上面提供的任何解决方案,如果你觉得你不需要正常访问这些文件,因为你总是可以通过 cPanel 等访问它们。

希望这个能帮上忙

您最好使用一个入口点构建应用程序,即所有文件都应该从 index.php 访问

把它放到 index.php 中

define(A,true);

这个检查应该在每个链接文件中运行(通过请求或包含)

defined('A') or die(header('HTTP/1.0 403 Forbidden'));

最简单的方法是将你的包含内容存储在 web 目录之外。这样服务器可以访问它们,但是不能访问外部机器。唯一的缺点是您需要能够访问服务器的这一部分。好处是它不需要设置、配置或额外的代码/服务器压力。

if (basename($_SERVER['PHP_SELF']) == basename(__FILE__)) { die('Access denied'); };

我的答案在方法上有些不同,但包括了这里提供的许多答案。我建议采取多管齐下的办法:

  1. . htaccess 和 Apache 限制是肯定的
  2. defined('_SOMECONSTANT') or die('Hackers! Be gone!');

然而,defined or die方法有许多缺点。首先,测试和调试的假设是一个真正的痛苦。其次,如果你改变了主意,它涉及到可怕的、令人麻木的无聊的重构。“找到并替换!”你说。是的,但是你有多少把握它在任何地方都写得一模一样,嗯?现在把它和成千上万的文件放在一起,哦

还有。Htaccess.如果您的代码分发到管理员不那么谨慎的站点上,会发生什么情况?如果你只依靠。Htaccess 来保护你的文件你还需要 a)备份,b)一盒纸巾来擦眼泪,c)一个灭火器来扑灭所有来自使用你代码的人的仇恨邮件。

所以我知道这个问题要求“最简单的”,但我认为这需要更多的“防御性编码”。

我的建议是:

  1. 在任何脚本 require('ifyoulieyougonnadie.php');之前(没有 include()和作为 defined or die的替代品)
  2. ifyoulieyougonnadie.php中,做一些逻辑工作——检查不同的常量、调用脚本、本地主机测试等等——然后实现 die(), throw new Exception, 403,等等。

    我正在用两个可能的入口点创建我自己的框架——主 index.php (Joomla 框架)和 ajaxrouter.php (我的框架)——因此根据入口点,我检查不同的东西。如果对 ifyoulieyougonnadie.php的请求不是来自这两个文件中的任何一个,我就知道有人在搞鬼!

    但是如果我添加一个新的入口点呢?别担心。我只是改变 ifyoulieyougonnadie.php,并且我已经排序,加上没有‘查找和替换’。万岁!

    如果我决定移动一些脚本来做一个不具有相同常量 defined()的不同框架会怎么样?万岁!^ _ ^

我发现这种策略让开发变得有趣得多,却让开发变得不那么有趣:

/**
* Hmmm... why is my netbeans debugger only showing a blank white page
* for this script (that is being tested outside the framework)?
* Later... I just don't understand why my code is not working...
* Much later... There are no error messages or anything!
* Why is it not working!?!
* I HATE PHP!!!
*
* Scroll back to the top of my 100s of lines of code...
* U_U
*
* Sorry PHP. I didn't mean what I said. I was just upset.
*/


// defined('_JEXEC') or die();


class perfectlyWorkingCode {}


perfectlyWorkingCode::nowDoingStuffBecauseIRememberedToCommentOutTheDie();
<?php
$url = 'http://' . $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI'];
if (false !== strpos($url,'.php')) {
die ("Direct access not premitted");
}
?>

1: 检查所包含文件的计数

if( count(get_included_files()) == ((version_compare(PHP_VERSION, '5.0.0', '>='))?1:0) )
{
exit('Restricted Access');
}

逻辑: 如果没有满足最小包含计数,PHP 退出。注意,在 PHP5之前,基页不被认为是一个包含。


2: 定义和验证全局常量

// In the base page (directly accessed):
define('_DEFVAR', 1);


// In the include files (where direct access isn't permitted):
defined('_DEFVAR') or exit('Restricted Access');

逻辑: 如果未定义常量,则执行不会从基页开始,PHP 将停止执行。

注意 为了便于跨越升级和未来的更改,使这种身份验证方法模块化将大大减少编码开销,因为这些更改不需要硬编码到每个文件。

// Put the code in a separate file instead, say 'checkdefined.php':
defined('_DEFVAR') or exit('Restricted Access');


// Replace the same code in the include files with:
require_once('checkdefined.php');

通过这种方式,可以向 checkdefined.php < em > 添加额外的代码,用于日志记录和分析目的,以及生成适当的响应。

值得称赞的地方: 可移植性的绝妙想法来自 这个答案。但是这个方法有一个缺点。不同文件夹中的文件可能需要不同的地址来寻址此文件。如果您在主站点的子文件夹中运行当前网站,则基于服务器根目录的寻址可能无法工作。


3: 远程地址授权

// Call the include from the base page(directly accessed):
$includeData = file_get_contents("http://127.0.0.1/component.php?auth=token");


// In the include files (where direct access isn't permitted):
$src = $_SERVER['REMOTE_ADDR']; // Get the source address
$auth = authoriseIP($src); // Authorisation algorithm
if( !$auth ) exit('Restricted Access');

此方法的缺点是独立执行,除非内部请求提供了会话令牌。在单个服务器配置的情况下,通过回路地址进行验证,或者通过多服务器或负载平衡的服务器基础设施的地址白名单进行验证。


4: 令牌授权

与前面的方法类似,可以使用 GET 或 POST 将授权令牌传递给 include 文件:

if($key!="serv97602"){header("Location: ".$dart);exit();}

一个非常混乱的方法,但也可能是最安全和通用的同时,在正确的方式使用。


5: Webserver 特定配置

大多数服务器允许您为单个文件或目录分配权限。您可以将所有的包含放在这些受限制的目录中,并配置服务器来拒绝它们。

例如,在 APACHE 中,配置存储在 .htaccess文件中。

但是,我不推荐使用特定于服务器的配置,因为它们不利于不同 Web 服务器之间的可移植性。在像 Content Management Systems 这样的情况下,拒绝算法很复杂,或者拒绝目录的列表相当大,这可能只会使重新配置会话相当可怕。最后,最好用代码来处理这个问题。


6: 将站点根目录放在一个安全的目录 OUTSIDE 中

由于服务器环境中的访问限制,这是最不受欢迎的方法,但是如果您有访问文件系统的权限,这是一种相当强大的方法。

//Your secure dir path based on server file-system
$secure_dir=dirname($_SERVER['DOCUMENT_ROOT']).DIRECTORY_SEPARATOR."secure".DIRECTORY_SEPARATOR;
include($secure_dir."securepage.php");

逻辑:

  • 用户不能要求任何文件以外的 htdocs文件夹的链接将在网站的地址系统的范围之外。
  • Php 服务器本地访问文件系统,因此可以访问计算机上的文件,就像具有所需特权的普通程序可以访问计算机上的文件一样。
  • 通过将 include 文件放在这个目录中,可以确保 php 服务器访问它们,同时拒绝用户访问热链接。
  • 即使 webserver 的文件系统访问配置不正确,这种方法也会防止这些文件意外地公开。

请原谅我非正统的编码习惯。任何反馈都是值得赞赏的。

如果更确切地说,你应该使用这个条件:

if (array_search(__FILE__, get_included_files()) === 0) {
echo 'direct access';
}
else {
echo 'included';
}

Get _ include _ files () 返回包含所有包含的文件名的索引数组(如果文件被执行,那么它被包含并且它的名字在数组中)。 因此,当直接访问该文件时,它的名称是数组中的第一个,数组中的所有其他文件都包含在内。

我觉得.htaccess 的建议不是很好,因为它可能会阻塞 该文件夹中您可能希望允许用户访问的其他内容, 这是我的解决办法:

$currentFileInfo = pathinfo(__FILE__);
$requestInfo = pathinfo($_SERVER['REQUEST_URI']);
if($currentFileInfo['basename'] == $requestInfo['basename']){
// direct access to file
}
if ( ! defined('BASEPATH')) exit('No direct script access allowed');

能顺利完成任务

前面提到的解决方案添加了 PHP 版本检查:

    $max_includes = version_compare(PHP_VERSION, '5', '<') ? 0 : 1;
if (count(get_included_files()) <= $max_includes)
{
exit('Direct access is not allowed.');
}

你可以使用 phpMyAdmin 样式:

/**
* block attempts to directly run this script
*/
if (getcwd() == dirname(__FILE__)) {
die('Attack stopped');
}

从该文件重定向到其他页面(如 index.html)

. htaccess:

Redirect 301 LINK_TO_YOUR_PHP LINK_TO_INDEX.HTML

我想限制直接访问 PHP文件,但也能够通过 jQuery $.ajax (XMLHttpRequest)调用它。以下是对我有效的方法。

if (empty($_SERVER["HTTP_X_REQUESTED_WITH"]) && $_SERVER["HTTP_X_REQUESTED_WITH"] != "XMLHttpRequest") {
if (realpath($_SERVER["SCRIPT_FILENAME"]) == __FILE__) { // direct access denied
header("Location: /403");
exit;
}
}

将你的包含文件存储在 web 可访问的目录之外已经被提到过好几次了,而且在可能的情况下是一个很好的策略。但是,我还没有看到提到的另一个选项: 确保您的包含文件 不包含任何可运行代码。如果您的包含文件只是定义函数和类,并且除此之外没有其他代码,那么当直接访问它们时,它们只会产生一个空白页面。

一定要允许从浏览器直接访问这个文件: 它 什么也做不了。它定义了一些函数,但是没有调用它们,所以它们都不能运行。

<?php


function a() {
// function body
}


function b() {
// function body
}

这同样适用于只包含 PHP 类而不包含其他内容的文件。


在可能的情况下,将文件保存在 web 目录之外仍然是一个好主意。

  • 您可能会意外地停用 PHP,在这种情况下,您的服务器可能会将 PHP 文件的内容发送到浏览器,而不是运行 PHP 并发送结果。这可能导致代码(包括数据库密码、 API 密钥等)泄漏。
  • 网络目录中的文件占用了你可能想用于你的应用程序的 URL。我使用的 CMS 不能有一个称为 system的页面,因为这将与代码使用的路径冲突。我觉得这很烦人。