阻止直接访问php包含文件

时间:2009-01-03 18:07:12

标签: php include include-guards

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

基本上我需要在php文件中进行如下检查:

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

有一种简单的方法吗?

33 个答案:

答案 0 :(得分:172)

将此添加到您只想包含的页面

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

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

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

答案 1 :(得分:156)

在您可能或可能无法完全控制的Apache服务器上运行的通用“PHP应用程序”最简单的方法是将您的包含放在目录中,并拒绝访问.htaccess文件中的该目录。为了省去Googling的麻烦,如果您使用的是Apache,请将其放在您不希望访问的目录中名为“.htaccess”的文件中:

Deny from all

如果你真的完全控制了服务器(这些天甚至比我第一次写这个答案的时候更常见),最好的方法是将你想要保护的文件粘贴到你的web服务器的目录之外正在服务。因此,如果您的应用位于/srv/YourApp/,请将服务器设置为从/srv/YourApp/app/投放文件并将包含放在/srv/YourApp/includes中,因此确实没有任何可以访问它们的网址。

答案 2 :(得分:105)

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

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

正在访问的文件始终是包含文件,因此== 1.

答案 3 :(得分:31)

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

我通常会一路走下去,并将所有PHP文件放在文档根目录之外,而不是bootstrap file - 文档根目录中唯一的index.php,它开始路由整个网站/应用程序。< / p>

答案 4 :(得分:25)

Chuck解决方案的另一种选择(或补充)是通过在.htaccess文件中添加类似的内容来拒绝访问与特定模式匹配的文件

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

答案 5 :(得分:24)

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将停止执行。


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将授权令牌传递给包含文件:

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

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


5:特定于Web服务器的配置

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

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

注意 但是我不建议使用特定于服务器的配置,因为它们不适合跨不同Web服务器的可移植性。在拒绝算法很复杂或者拒绝目录列表相当大的情况下,它可能只会使重新配置会话变得非常可怕。最后,最好在代码中处理此问题。


6:放置包含在站点根目录外的安全目录中

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

//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服务器本地访问文件系统,因此可以访问计算机上的文件,就像具有所需权限的普通程序一样。
  • 通过将包含文件放在此目录中,您可以确保php服务器可以访问它们,同时拒绝用户进行热链接。
  • 即使网络服务器的文件系统访问配置未正确完成,此方法也会阻止这些文件意外泄露。

请原谅我的非正统编码约定。任何反馈都表示赞赏。

答案 6 :(得分:15)

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

  • 将文档放在webroot之外,或者放在Web服务器拒绝访问的目录中 AND
  • 在可见文档中使用隐藏文档检查的定义:
      if (!defined(INCL_FILE_FOO)) {
          header('HTTP/1.0 403 Forbidden');
          exit;
      }

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

答案 7 :(得分:7)

我曾经遇到过这个问题,解决了:

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

但理想的解决方案是将文件放在Web服务器文档根目录之外,如另一个anwser中所述。

答案 8 :(得分:5)

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

到达

将其放在index.php

define(A,true);

此检查应在每个链接文件中运行(通过require或include)

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

答案 9 :(得分:4)

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

答案 10 :(得分:4)

我想直接限制对 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;
    }
}

答案 11 :(得分:4)

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

$including = true;

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

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

答案 12 :(得分:4)

Joomla!确实是在根文件中定义一个常量并检查是否在包含的文件中定义了它。

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

或者

通过将它们放在webroot目录之外,可以将所有文件保留在http请求范围之外,就像大多数框架一样,如CodeIgniter建议的那样。

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

答案 13 :(得分:3)

除了.htaccess方式之外,我在各种框架中看到了一个有用的模式,例如在rails上的ruby中。它们在应用程序根目录中有一个单独的pub /目录,并且库目录与pub /一起位于相同级别的目录中。这样的事情(不理想,但你明白了):

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

您将Web服务器设置为使用pub /作为文档根目录。这为您的脚本提供了更好的保护:虽然他们可以从文档根目录进行访问以加载必要的组件,但是无法从Internet访问组件。除安全性之外的另一个好处是一切都在一个地方。

此设置优于仅在每个包含的文件中创建检查,因为“不允许访问”消息是攻击者的线索,并且它优于.htaccess配置,因为它不是基于白名单的:如果你搞砸了文件扩展名在lib /,conf / etc.目录中不可见。

答案 14 :(得分:2)

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

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

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

答案 15 :(得分:1)

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

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

答案 16 :(得分:1)

<?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
?>

答案 17 :(得分:1)

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

答案 18 :(得分:1)

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

  1. .htaccess和Apache肯定的限制
  2. defined('_SOMECONSTANT') or die('Hackers! Be gone!');
  3. defined or die方法有许多失败。首先,测试和调试的假设真的很痛苦。其次,如果你改变主意,它会涉及可怕的,令人厌烦的无聊重构。 “查找和替换!”你说。是的,但你有多确定它在各处都写得完全一样,嗯?现在将其与数千个文件相乘...... o.O

    然后就是.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()的不同框架,该怎么办? ......万岁! ^ _ ^

    3. 我发现这种策略使开发变得更有趣,更少:

      /**
       * 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();
      

答案 19 :(得分:0)

已经多次提到了将包含文件存储在Web可访问目录之外的方法,并且在可能的情况下肯定是一个不错的策略。但是,我还没有提到另一种选择:确保您的包含文件不包含任何可运行的代码。如果您的包含文件仅定义函数和类,而没有其他代码,则直接访问它们只会产生一个空白页。

从所有方面说,允许从浏览器直接访问此文件:它不会执行任何操作。它定义了一些函数,但是没有一个被调用,因此它们都没有运行。

<?php

function a() {
    // function body
}

function b() {
    // function body
}

这同样适用于仅包含PHP类而没有其他内容的文件。


将文件尽可能保留在网络目录之外仍然是一个好主意。

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

答案 20 :(得分:0)

这就是Google在其PHP示例see here

中使用的内容
if (php_sapi_name() != 'cli') {
  throw new \Exception('This application must be run on the command line.');
}

答案 21 :(得分:0)

做类似的事情:

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

答案 22 :(得分:0)

您可以使用phpMyAdmin Style:

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

答案 23 :(得分:0)

if ( ! defined('BASEPATH')) exit('No direct script access allowed');

将顺利完成工作

答案 24 :(得分:0)

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

答案 25 :(得分:0)

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

答案 26 :(得分:0)

你还可以做的是密码保护目录,并保留所有的PHP脚本,除了index.php文件之外,因为在包含密码时不需要,因为它只需要http访问。如果您需要它,它将为您提供访问脚本的选项,因为您将拥有访问该目录的密码。您需要为目录设置.htaccess文件,并使用.htpasswd文件对用户进行身份验证。

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

希望这有帮助

答案 27 :(得分:0)

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

答案 28 :(得分:0)

你可以使用下面的方法,虽然它有一个缺陷,因为它可以伪造,除非你可以添加另一行代码以确保请求只来自你的服务器使用Javascript。 您可以将此代码放在HTML代码的“正文”部分中,以便显示错误。

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

将您的其他HTML代码放在此处

<? } ?>

这样结束,所以错误的输出将始终显示在正文部分中,如果这就是你想要的那样。

答案 29 :(得分:0)

我发现这个只有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模式。

答案 30 :(得分:-1)

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

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

答案 31 :(得分:-2)

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

htaccess的:

Redirect 301 LINK_TO_YOUR_PHP LINK_TO_INDEX.HTML

答案 32 :(得分:-7)

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