• 4
name

A PHP Error was encountered

Severity: Notice

Message: Undefined index: userid

Filename: views/question.php

Line Number: 191

Backtrace:

File: /home/prodcxja/public_html/questions/application/views/question.php
Line: 191
Function: _error_handler

File: /home/prodcxja/public_html/questions/application/controllers/Questions.php
Line: 433
Function: view

File: /home/prodcxja/public_html/questions/index.php
Line: 315
Function: require_once

name Punditsdkoslkdosdkoskdo

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 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/
  • 53
Reply Report

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.

  • 14
Reply Report

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.

  • 14
Reply Report
      • 2
    • Agreed; if anyone manages to guess the name of your include file that has only functions and classes, all they'd see is a blank page when trying to view it. If you're outputting html with an include (which I don't like to do) you can instead put it into a function, include the file, and call the function.
      • 1
    • This is a good solution, but beware: if you leave your scripts in the public directory, remember to either a) make sure you don't have any syntax errors or b) turn off display errors, because otherwise they will still show up, even if the file only contains functions/classes.
    • I feel it should be pointed out that the technique is useful for more than just blocking direct access. Using the "needs external variable" technique, you can give a file different functionality when accessed directly. So, for example, you can easily pull in variables from post and run the primary function rather than just waiting for functions to be called and print a response, allowing you to turn it into an easy REST interface.
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
  • 11
Reply Report

Just store the file outside your web root.

  • 10
Reply Report
    • @liljoshu Yes, and frameworks like Laravel store the majority of your files ... guess where, outside the web root.
      • 2
    • @Erik yesterday If you have the luxury of working on a site that was designed properly or you had the benefit of designing the site yourself, which is not always the case, that may be the case. The last Laravel I worked with had nothing outside of web root, sadly. And even then, not all files are outside of web root. Sometimes you need to work in the web root either because situation forces it or that's how a framework functions. It's definitely a good answer. Just not the only good answer.
      • 1
    • Frameworks do not have any say in where I put my code. Anything that prevents basic security measures is to be avoided.
      • 1
    • Not really. From experience, some modules in some frameworks insist that your additional code be placed in specific locations, such as Laravel.
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.)

  • 6
Reply Report
      • 2
    • I don't understand how this is a vulnerability. site.com/myfile.php/badstring.php simply results in a 404 error, in a simple install.
      • 2
    • php_self is set by the agent based on the request URI. /index.php/dummy_hijack.php will not resolve /index.php. $_SERVER['DOCUMENT_ROOT'].$_SERVER['SCRIPT_NAME'] does. Your example is safe in a way, but not accurate. users who use basename are in trouble using PHP_SELF though. if (basename(FILE) == basename($_SERVER['PHP_SELF']))
      • 1
    • Okay. I get it now. Feel free to edit my post to add a warning. I don't know if I can explain it right myself.
      • 1
    • Seems like this solution would be fine for when the program can either be included or directly accessed and you need a way to detect which method is being used.

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

if ( empty(get_included_files()) ) die("Direct access forbidden");
  • 6
Reply Report
      • 1
    • Should be noted as of PHP5 this condition would never pass. See this comment in the docs. The current file is always in the array, so the array always has at least 1 item ergo would never be empty. In PHP5+, one could try if ( count(get_included_files()) === 1 ) die("Direct access forbidden"); instead.

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.

  • 2
Reply Report
      • 2
    • This is the more "standard" way in most business environments that use the LAMP stack (which is where you'll see PHP the most in the business world.)

Check how many included files are there...

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

Or how many minimum there should be rather than 2

  • 2
Reply Report

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.

  • 1
Reply Report

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/

  • 1
Reply Report

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
*/
?>
  • 0
Reply Report

Warm tip !!!

This article is reproduced from Stack Exchange / Stack Overflow, please click

Trending Tags

Related Questions