GUIpsp
GUIpsp

Reputation: 161

How to detect if a file is being included or directly ran

I have a php file that I include in my php script, but I don't want people to be able to directly run the file(without being included). How can I prevent that from happening?

Upvotes: 5

Views: 4233

Answers (8)

tim
tim

Reputation: 2724

Checking if the script is the parent of the PHP process might not be the best idea for preventing users of requesting an include file directly. But it can be handy in many other cases i.e. AJAX modules etc. I'm not gonna start a new topic by this.

if (__FILE__ == get_included_files()[0])
// Doesn't work with PHP prepend unless calling [1] instead.

if (__FILE__ == $_SERVER['SCRIPT_FILENAME'])
// May not work on Windows due to mixed DIRECTORY_SEPARATOR (machine specific)

if (basename(__FILE__) == basename($_SERVER['SCRIPT_FILENAME']))
// Doesn't work with files with the same basename but different paths

if (defined('FLAG_FROM_A_PARENT'))
// Works in all scenarios but I personally dislike this

if (realpath(__FILE__) == realpath($_SERVER['DOCUMENT_ROOT'] . $_SERVER['SCRIPT_FILENAME']))
// Or, if your server already put document root in SCRIPT_FILENAME:
if (realpath(__FILE__) == realpath($_SERVER['SCRIPT_FILENAME']))

Keep in mind some machines that use virtual paths may return different paths in different php components. realpath() is your friend for this.

Do NOT EVER attempt these:

// NO GO: Unsafe user input!!
if (realpath(__FILE__) == realpath($_SERVER['DOCUMENT_ROOT'] . $_SERVER['PHP_SELF']))
if (realpath(__FILE__) == realpath($_SERVER['DOCUMENT_ROOT'] . $_SERVER['REQUEST_URI']))

Upvotes: 8

adilbo
adilbo

Reputation: 970

I know this question is old and already answered, but nothing works for me nicely. So I found the following solution and like to post it here!

It also works in cli:

if ( __FILE__ == __DIR__ . DIRECTORY_SEPARATOR . basename($_SERVER['PHP_SELF']) ) {

  die('<tt><h1>You cannot access the specified file!');

}

Upvotes: 0

Brad
Brad

Reputation: 950

Easy, use the debug backtrace 😎.

$caller = debug_backtrace()[0] ?? null;
if ($caller && in_array($caller['function'] ?? 'unknown', ['include', 'require', 'include_once', 'require_once'])) {
    echo 'This file was included/required by ' . $caller['file'];
}

No need to define constants in all your other files before including it or picking apart the request URI or falling into any traps with bad file name comparisons.

Plus this will also work for PHP run via commandline.

Note that I answered the question title and not the use case.

For the reason why you want to detect this, the better answer is:

Move the sensitive PHP files outside the public space.

/website/
|- public/
  |- public files...
|- source
  |- private include files...

If your web server forces you to have all files in the public folder, then create a subfolder and then use a .htaccess file to block anyone from accessing anything inside it.

#inside of '/website/public/source/.htaccess`
Deny from all

Upvotes: 1

ImmortalPC
ImmortalPC

Reputation: 1690

2 solutions for a good protection:

  1. Apache

with htaccess:

RedirectMatch 404 "\.(sql|sh|java|class|inc\.php)$"

Or in /etc/apache2/sites-enabled:

<VirtualHost *:80>
#...
RedirectMatch 404 "\.(sql|sh|java|class|inc\.php)$"
#...
</VirtualHost>

Then name your file like this: myInternalFileIncludeOnly.inc.php

  1. PHP

With this sample code, PHP is able to detect include:

if( get_included_files()[0] == __FILE__ ){
    echo '2.php direct access';
}else{
    echo '2.php was included';
}

EDIT: See Tim answer, so if you have a prepend include (cf php.ini) use this:

if(
    (!ini_get('auto_prepend_file') && get_included_files()[0] === __FILE__)
    ||
    ini_get('auto_prepend_file') && (($__FILE__=str_replace('\\','/',__FILE__)) === str_replace('\\','/',$_SERVER['DOCUMENT_ROOT'].$_SERVER['SCRIPT_FILENAME']) || $__FILE__ === str_replace('\\','/',$_SERVER['SCRIPT_FILENAME']) )
)
    echo '2.php direct access',"\n";

Upvotes: 3

Lightness Races in Orbit
Lightness Races in Orbit

Reputation: 385098

Included PHP files should contain class/function/variable definitions, not inline logic/output.

Running them directly is then, essentially, a no-op.

Upvotes: 0

Frank Farmer
Frank Farmer

Reputation: 39356

I've seen this pattern in some applications:

  1. Main, directly run file runs define('SOMETHING', 'SOMEVALUE');
  2. Included file starts with if(!defined('SOMETHING')) return;

Upvotes: 1

ThiefMaster
ThiefMaster

Reputation: 318468

Make the included scripts not accessible via HTTP at all. E.g. by protecting the subfolder or moving them above the document root.

If you cannot do that, define() something like IS_INCLUDED in your main script and exit; if this constant is not defined() in your included script.

Upvotes: 7

gd1
gd1

Reputation: 11403

I use a sentinel constant, defined in the "including" script, and the "included" script looks for it.

Example:

/* main.php */
define('SENTINEL', 1);
include "included.inc.php"

/* included.inc.php */
if (!defined('SENTINEL')) die("Wanted to be smart?? Nice try.");

Upvotes: 3

Related Questions