176

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

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

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

是否有捷径可寻?

4

33 回答 33

212

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

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

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

<?php
define('MyConst', TRUE);
?>
于 2009-01-03T18:15:10.683 回答
183

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

Deny from all

如果您实际上可以完全控制服务器(这些天甚至对于小应用程序比我第一次写这个答案时更常见),最好的方法是将要保护的文件粘贴到您的 Web 服务器提供服务的目录之外. 因此,如果您的应用程序位于 中/srv/YourApp/,请将服务器设置为从其中提供文件/srv/YourApp/app/并将包含内容放入 中/srv/YourApp/includes,因此实际上没有任何 URL 可以访问它们。

于 2009-01-03T18:14:15.863 回答
120

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

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

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

于 2009-01-03T20:09:39.237 回答
38

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 用于记录和分析目的,以及生成适当的响应。

应得的信用:可移植性的绝妙想法来自这个答案。然而,这种方法有一个缺点。不同文件夹中的文件可能需要不同的地址来寻址该文件。如果您从主站点的子文件夹中运行当前网站,则基于服务器根的寻址可能不起作用。


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:Webserver具体配置

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

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

但是请注意,我不推荐特定于服务器的配置,因为它们不利于跨不同 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 服务器可以访问它们,同时拒绝用户进行热链接。
  • 即使网络服务器的文件系统访问配置没有正确完成,这种方法也可以防止这些文件意外公开。

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

于 2014-03-27T05:00:56.170 回答
37

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

我通常一路走下去,将我所有的 PHP 文件放在文档根目录之外,除了引导文件- 文档根目录中的一个单独的 index.php 开始路由整个网站/应用程序。

于 2009-01-03T18:14:13.620 回答
30

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

<FilesMatch "\.(inc)$">
    Order deny,allow
    Deny from all
</FilesMatch>
于 2009-01-03T18:48:18.010 回答
14

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

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

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

于 2009-01-03T19:00:08.300 回答
8

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

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

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

于 2010-03-09T21:56:46.180 回答
8

我想直接限制对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;
    }
}
于 2017-11-08T08:27:09.427 回答
7

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

把它放在 index.php 中

define(A,true);

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

defined('A') or die(header('HTTP/1.0 403 Forbidden'));
于 2013-03-28T23:51:15.757 回答
4

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

$including = true;

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

if (!$including) exit("direct access not permitted");
于 2009-01-03T18:12:21.813 回答
4
debug_backtrace() || die ("Direct access not permitted");
于 2012-01-22T23:27:32.213 回答
4

我的答案在方法上有所不同,但包括此处提供的许多答案。我会推荐一个多管齐下的方法:

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

然而,这种defined or die方法有许多失败之处。首先,测试和调试的假设是一个真正的痛苦。其次,如果你改变主意,它会涉及到可怕的、令人麻木的、无聊的重构。“查找和替换!” 你说。是的,但是你有多确定它在任何地方都写得完全一样,嗯?现在将它与数千个文件相乘...... oO

然后是.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();
于 2013-12-06T09:48:38.283 回答
3

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

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

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

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

于 2009-01-04T13:06:53.613 回答
3

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

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

要不然

正如大多数框架(如 CodeIgniter)所推荐的那样,可以通过将所有文件放在 webroot 目录之外来将所有文件放在 http 请求的范围之外。

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

于 2010-05-07T13:11:11.747 回答
2

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

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

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

于 2015-05-08T21:37:03.793 回答
1

执行以下操作:

<?php
if ($_SERVER['SCRIPT_FILENAME'] == '<path to php include file>') {
    header('HTTP/1.0 403 Forbidden');
    exit('Forbidden');
}
?>
于 2009-01-03T18:18:48.117 回答
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
?>
于 2010-05-07T13:00:37.817 回答
1

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

if ( strpos(strtolower($_SERVER['SCRIPT_NAME']),strtolower(basename(__FILE__))) )
{
    header("Location: ../../index.php");
    die("...");
}
于 2010-07-18T00:44:38.763 回答
1

我发现这个仅适用于 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 模式。

于 2011-02-16T16:08:30.693 回答
1
if (basename($_SERVER['PHP_SELF']) == basename(__FILE__)) { die('Access denied'); };
于 2013-10-01T11:17:53.280 回答
1
<?php       
$url = 'http://' . $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI'];
  if (false !== strpos($url,'.php')) {
      die ("Direct access not premitted");
  }
?>
于 2014-03-08T04:42:26.697 回答
1

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

无论如何允许从浏览器直接访问这个文件:它不会做任何事情。它定义了一些函数,但没有一个被调用,所以它们都没有运行。

<?php

function a() {
    // function body
}

function b() {
    // function body
}

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


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

  • 您可能不小心停用了 PHP,在这种情况下,您的服务器可能会将 PHP 文件的内容发送到浏览器,而不是运行 PHP 并发送结果。这可能会导致您的代码(包括数据库密码、API 密钥等)泄露。
  • web 目录中的文件占用了您可能希望用于您的应用程序的 URL。我使用的 CMS 不能有一个名为 的页面system,因为这会与用于代码的路径冲突。我觉得这很烦人。
于 2019-03-08T15:35:33.950 回答
0

您可以使用下面的方法,但它确实存在缺陷,因为它可以被伪造,除非您可以添加另一行代码以确保请求仅来自您的服务器,或者使用 Javascript。您可以将此代码放在 HTML 代码的 Body 部分中,以便在那里显示错误。

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

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

<? } ?>

像这样结束它,因此错误的输出将始终显示在正文部分中,如果这是您想要的那样。

于 2011-09-10T00:53:10.860 回答
0

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

于 2012-04-23T15:56:45.833 回答
0

您还可以做的是密码保护目录并将所有 php 脚本保存在其中,当然除了 index.php 文件,因为在包含密码时不需要,因为它仅用于 http 访问。它还会为您提供访问脚本的选项,以防您需要它,因为您将拥有访问该目录的密码。您需要为该目录​​设置 .htaccess 文件和一个 .htpasswd 文件来验证用户身份。

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

希望这可以帮助

于 2013-02-05T17:30:26.227 回答
0

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

于 2013-08-14T14:34:13.413 回答
0

我没有发现 .htaccess 的建议这么好,因为它可能会阻止该文件夹中您可能希望允许用户访问的其他内容,这是我的解决方案:

$currentFileInfo = pathinfo(__FILE__);
$requestInfo = pathinfo($_SERVER['REQUEST_URI']);
if($currentFileInfo['basename'] == $requestInfo['basename']){
    // direct access to file
}
于 2015-09-10T08:17:30.443 回答
0

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

    $max_includes = version_compare(PHP_VERSION, '5', '<') ? 0 : 1;
    if (count(get_included_files()) <= $max_includes)
    {
        exit('Direct access is not allowed.');
    }
于 2015-12-17T19:16:56.343 回答
0

您可以使用 phpMyAdmin 样式:

/**
 * block attempts to directly run this script
 */
if (getcwd() == dirname(__FILE__)) {
    die('Attack stopped');
}
于 2017-01-31T08:23:43.043 回答
-1
if ( ! defined('BASEPATH')) exit('No direct script access allowed');

会顺利完成工作

于 2015-10-16T12:38:13.820 回答
-1

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

.ht 访问:

Redirect 301 LINK_TO_YOUR_PHP LINK_TO_INDEX.HTML
于 2017-05-27T20:06:31.733 回答
-7

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

于 2011-10-02T15:50:32.457 回答