PHP: Проверьте, загружен ли файл напрямую, а не в том числе?

Есть ли способ запретить пользователю просмотр файла, но при этом использовать его как включенный в другой файл в PHP?

16 ответов

Решение

Если вы используете

define('APP_RAN'); 

в файле, который включает его, а затем положить

if(!defined('APP_RAN')){ die(); }

или в качестве альтернативы

defined('APP_RAN') or die();

(который легче читать)

во включенных файлах он умрет, если вы получите к ним доступ напрямую.


Вероятно, было бы лучше поместить все ваши включенные файлы выше вашего DocumentRoot.

Например, если ваша страница индекса находится на

/my/server/domain/public_html

Вы должны положить включенные файлы в

/my/server/domain/

Мое предложение:

<?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.) он выводит стандартную страницу ошибки Apache 404 (пожалуйста, сравните ее с исходной страницей 404 или просто включите эту страницу), чтобы повысить безопасность за счет неясности

3.) else-часть предотвращает частичное выполнение, пока файл загружается в живую среду (PHP не ожидает "?>"). Вам это не нужно, если ваш включенный файл содержит только одну функцию / один класс.

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

Не используйте глобальный код в ваших файлах, только функции и методы. Тогда не нужно будет заботиться о включении или прямом использовании.

Просто сохраните файл за пределами вашего веб-корня.

if (__FILE__ == $_SERVER['DOCUMENT_ROOT'].$_SERVER['PHP_SELF']){
  die("Direct access forbidden");
}

Это работает независимо от того, где это. (Приветствует @col-sharpnel за указание на неполное, но хорошее решение.)

Есть функция get_included_files. Вы можете использовать это так:

if ( empty(get_included_files()) ) die("Direct access forbidden");

Проверьте, сколько есть включенных файлов...

if(count(get_required_files()) < 2) { die(); }

Или сколько минимум должно быть, а не 2

Под Apache это легко: добавить <Files> директива в вашем.htaccess для предотвращения доступа к нему из веб-браузера. Это не относится к PHP includesи рекомендуется скрывать несколько файлов от доступа браузера (хотя обычно вы пытаетесь собрать все недоступные файлы в один каталог).

<Files="myprivatefile.php">
    deny from all
</Files>

На другом веб-сервере вы можете скрыть файл из корня документа, но в некоторых случаях (например, если ваши сценарии open_basedirв строгом смысле) не получится.

if($argv[0] == basename(__FILE__))
    include_once('/path/to/file.php');

Я бы пошел на решение Chacha102.

Кроме того, поскольку заголовок вашего вопроса говорит "как проверить", вы также можете сделать это, не определяя переменную с помощью

// secret.php is the name of this file.
if($_SERVER["SCRIPT_FILENAME"]=='secret.php') die();

Если вы хотите, чтобы оно не отображалось, когда оно не включено, используйте более автоматизированный способ.

if (basename(__FILE__) == basename($_SERVER['PHP_SELF'])) header("HTTP/1.0 404 Not Found");

Таким образом, он все равно будет работать, если вы в конечном итоге измените имя файла или что-то еще.

В этой теме есть много хороших ответов - вот тот, который еще не был упомянут.

Вы можете назвать ваши включенные файлы PHP с помощью i в расширении, например someincludedfile.phpi а затем настройте Apache так, чтобы он не работал phpi файлы. Вуаля.

Минусы:

  • (!) сильно зависит от конфигурации Apache (поэтому вы уязвимы, если кто-то пренебрегает этой строкой) - и в таком случае браузер может увидеть весь PHP-скрипт в виде открытого текста, поскольку он не будет передан PHP (действительно плохо!)
  • это может быть раздражающим, чтобы переименовать ваши включенные файлы

Плюсы:

  • дает понять, какие файлы должны быть включены, а какие нет в вашем коде
  • вам не нужно перемещать файловую иерархию

Лично я предпочел бы просто переместить файлы в каталог за пределами корня документа, но если у вас большое унаследованное приложение, это можно было бы изменить быстрее.

Ссылка: http://www.ducea.com/2006/07/21/apache-tips-tricks-deny-access-to-certain-file-types/

Просто расширить на решения с $_SERVER Переменные - ниже небольшой файл.php, а в комментариях - копия bash тест, который я сделал на Ubuntu; Дело в том, что эти переменные могут немного изменяться в зависимости от типа доступа и от того, используются ли символические ссылки (обратите внимание также, что в приведенном ниже коде я должен экранировать ' ?\> 'в операторе 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
*/
?>

На основе сообщения @HelpNeeder здесь. Будет работать независимо от каталога или контекста.

      // if we called this file directly, then we are using stdout
if (get_included_files()[0] === __FILE__) {

    /** @noinspection PhpUnhandledExceptionInspection */
    print json_encode($deploymentMatrix, JSON_THROW_ON_ERROR);

} else {

    return $deploymentMatrix;

}

Вы должны использовать PHP-кодировщик и загрузчик:

коммерческий

Свободно

Другие вопросы по тегам