开发者

PHP: Check if a file is loaded directly instead of including?

Is there a way to prevent a user viewing an file but still use it 开发者_C百科as included to another file in PHP?


If you use

define('APP_RAN'); 

in the file that includes it and then put

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

or alternatively

defined('APP_RAN') or die();

(which is easier to read)

in included files it would die if you access them directly.


It would probably be better to put all of your included files above your DocumentRoot though.

For example, if your index page is at

/my/server/domain/public_html

You should put the included files in

/my/server/domain/


My proposal:

<?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.) it checks if it is called directly else it throws an error

2.) it outputs a 404 standard apache error page (please compare with your original 404 page or simple include that page) to add security through obscurity

3.) The else-part avoids partial execution while the file is uploaded to the live environment (PHP doesn't wait for the "?>"). You don't need it if your included file contains only one function / one class.


Do not use any global code in your files, only functions and methods. Then there will be no need to care about include vs. direct use.


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


Just store the file outside your web root.


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

It works regardless of where it is. (Cheers to @col-sharpnel for pointing out an incomplete yet good solution.)


There's the get_included_files function. You could use it like so:

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


Under Apache, this is easy: add a <Files> directive in your .htaccess to prevent access to it from a web browser. This doesn't apply to PHP includes, and it is a good practice to hide several files from browser access (though usually you'll try to put all the non-accessible files together in a single directory).

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

Under a different web server, you can hide the file out of your document root, but under some cases (like if your scripts are open_basedir'd in a strict way), it won't work.


Check how many included files are there...

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

Or how many minimum there should be rather than 2


I'd go for Chacha102's solution.

Additionally, as your question title says «how to check», you can also do this without having to define a variable by using

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


If you want to stop it from ever being displayed when not included here's a more automated way.

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

This way it'll still work if you end up changing the file name or something.


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


There are many good answers in this thread - here's one that hasn't been mentioned yet.

You can name your included PHP files with an i in the extension, e.g. someincludedfile.phpi and then configure Apache so that it doesn't serve phpi files. Voila.

Cons:

  • (!) highly dependent on the Apache configuration (so you're vulnerable if someone neglects that line) -- and in such a event, the browser might get to see the whole PHP script in plaintext since it won't get passed off to PHP (really bad!)
  • it can be annoying to rename your included files

Pros:

  • makes it clear which files are to be included and which aren't in your code
  • you don't have to move your file hierarchy around

Personally, I would rather just move files into a directory outside the document root, but if you have a large legacy application, this could be quicker to change.

Link: http://www.ducea.com/2006/07/21/apache-tips-tricks-deny-access-to-certain-file-types/


Just to expand on the solutions with $_SERVER variables - below is a small .php file, and in the comments is a copy of a bash test I did on Ubuntu; the point being, that these variables can change quite a bit depending on the type of access, and whether symlinks are used (note also, in the code below I have to escape the '?\>' in the echo statement, else the syntax color breaks; remove the backslash if trying the code):

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


Based on @HelpNeeder's post here. Will work regardless of directory or context.

// 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;

}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜