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;
}
精彩评论