Proper Includes via $_GET

Discussions of secure PHP coding. Security in software is important, so don't be afraid to ask. And when answering: be anal. Nitpick. No security vulnerability is too small.

Moderator: General Moderators

User avatar
shiflett
Forum Contributor
Posts: 124
Joined: Sun Feb 06, 2005 11:22 am

Post by shiflett »

onion2k wrote:In general, unless I'm going to have millions of different include files, I use a switch
This is an excellent approach and one I often recommend. It's very clear, plus it doesn't depend on any variables. Hard coded strings are very safe and obvious.

In contrast, look at how unclear the other suggestions are in comparison.
nielsene wrote:Well you aren't stripping out "../"s so its possible that a file could escape the sandbox.
One security principle to which I adhere (or try to) is to never try to correct invalid data. When you do, you create an opportunity for you to make a mistake that yields a security vulnerability. For example, here is an approach I have observed quite frequently:

Code: Select all

$string = str_replace('..', '.', $tainted_string);
Can you think of a way around this?
nielsene wrote:It is forced to be a php file so they couldn't show the passwd file.
That's not true. The way include works is the file (or resource) you reference is treated just like any other PHP file. If there are no PHP blocks, it's treated as raw output.
Roja wrote:Thats why statements like "I've never had any problems" have no place in a discussion about security.
Roja++
Roja wrote:Correct, because you are going above the root - which makes no sense.
What he said isn't correct, because going above root doesn't fail. Maybe it does on Windows or something, but the parent directory of root is root.
Todd_Z wrote:If you tried to include /home/blah/public_html/../../index.php", you would get an error for file not being found.
I have no idea what you think you've proven, but try this:

/home/blah/public_html/../../../../../../../../../../etc/passwd

If you're on Windows, that won't work either, but maybe you get the idea.
Roja wrote:They can't VIEW them, they can cause them to be included/executed.
Includes are treated just like any other PHP file. The only code that is executed is within PHP blocks. Everything else is just raw output.

So, if you're referring to his example of index.php, you're right. If this is the case, just be careful to qualify your statements. :-)

Hope that helps.
Roja
Tutorials Group
Posts: 2692
Joined: Sun Jan 04, 2004 10:30 pm

Post by Roja »

shiflett wrote:
Roja wrote:Correct, because you are going above the root - which makes no sense.
What he said isn't correct, because going above root doesn't fail. Maybe it does on Windows or something, but the parent directory of root is root.
Actually, going above the root does fail. If you use the correct number of parents, you can explore to directories next to the directory in question.
shiflett wrote:
Todd_Z wrote:If you tried to include /home/blah/public_html/../../index.php", you would get an error for file not being found.
I have no idea what you think you've proven, but try this:

/home/blah/public_html/../../../../../../../../../../etc/passwd

If you're on Windows, that won't work either, but maybe you get the idea.
My code sample shows that you can use parent dirs (../), which previous posts claimed you couldn't do ("You can't access anything but the directory"). It allows you to go above, sideways, and below. By using the absolute value from the root, you can go all the way up, and all the way down.

From my reading, we actually agree, but your phrasing seems to indicate that you didn't quite understand what I meant. :)
User avatar
shiflett
Forum Contributor
Posts: 124
Joined: Sun Feb 06, 2005 11:22 am

Post by shiflett »

Roja wrote:Actually, going above the root does fail.
On what platform? I'm not aware of any platform that has this behavior.
User avatar
nielsene
DevNet Resident
Posts: 1834
Joined: Fri Aug 16, 2002 8:57 am
Location: Watertown, MA

Post by nielsene »

shiflett wrote: In contrast, look at how unclear the other suggestions are in comparison.
nielsene wrote:Well you aren't stripping out "../"s so its possible that a file could escape the sandbox.
One security principle to which I adhere (or try to) is to never try to correct invalid data. When you do, you create an opportunity for you to make a mistake that yields a security vulnerability. For example, here is an approach I have observed quite frequently:

Code: Select all

$string = str_replace('..', '.', $tainted_string);
Can you think of a way around this?
Yes I can. And yes normally I wouldn't correect the data, only detect and reject. (Ie check if filename = basename or reject) slightly strong versions can check for forward/backward slashes under a somewhat long set of encodings and reject in those as well.
nielsene wrote:It is forced to be a php file so they couldn't show the passwd file.
That's not true. The way include works is the file (or resource) you reference is treated just like any other PHP file. If there are no PHP blocks, it's treated as raw output.
Not true in this case. The OP was concatenating ".php" onto the user-provided name. That's what I mean by a PHP file, not a parsed file, but I can see the opportunity for confusion. Even if they asked for /etc/passwd it would end up being /etc/passwd.php
User avatar
nielsene
DevNet Resident
Posts: 1834
Joined: Fri Aug 16, 2002 8:57 am
Location: Watertown, MA

Post by nielsene »

shiflett wrote:
Roja wrote:Actually, going above the root does fail.
On what platform? I'm not aware of any platform that has this behavior.
Same here... The only exception I'm aware of is if you've mada chroot/jail.
User avatar
shiflett
Forum Contributor
Posts: 124
Joined: Sun Feb 06, 2005 11:22 am

Post by shiflett »

nielsene wrote:Not true in this case. The OP was concatenating ".php" onto the user-provided name. That's what I mean by a PHP file, not a parsed file, but I can see the opportunity for confusion.
I see now. Thanks.

An attacker can get around the .php concatenation on many platforms by using a NUL byte. For example:

http://example.org/test.php?p=../../../ ... /passwd%00

Try it - you might be surprised by the results.
User avatar
nielsene
DevNet Resident
Posts: 1834
Joined: Fri Aug 16, 2002 8:57 am
Location: Watertown, MA

Post by nielsene »

Yup I've seen that too...

All these little exceptions is why I don't do user-provided includes. If I had to, I'd use the basename function, coupled with a regexp ([-A-Za-z0-9_]), with forced ending (.php or .inc depending on your preference, etc‚). The null-byte, full stops. and slashes wouldn't survive that.

As always, state what you'll accept, not what you'll reject. Its too easy to miss something if you only list the bad stuff.
User avatar
Todd_Z
Forum Regular
Posts: 708
Joined: Thu Nov 25, 2004 9:53 pm
Location: U Michigan

Post by Todd_Z »

Oh, i see the security holes now. But no - I keep my server as clean as possible.
timvw
DevNet Master
Posts: 4897
Joined: Mon Jan 19, 2004 11:11 pm
Location: Leuven, Belgium

Post by timvw »

http://www.php.net/realpath can come in quite handy ;)
juglesh
Forum Newbie
Posts: 1
Joined: Wed Aug 17, 2005 9:53 am

Post by juglesh »

how's this when you are simply including basic pages?

Code: Select all

$_GET['page'] = ereg_replace("[^[:alnum:] ]","-",$_GET['page']);
also, I was wondering if there is any way around

Code: Select all

include 'myPrefix_'.$_GET['page'].'.php';
I have tried to ../ my way out of it, but it seems secure.
User avatar
nielsene
DevNet Resident
Posts: 1834
Joined: Fri Aug 16, 2002 8:57 am
Location: Watertown, MA

Post by nielsene »

Well the null byte can stop the .php. I'm not sure if an attacker could get a series of ^H control code in to delete the prefix, but that might be possible via some encoding or another.
DeprecatedDiva
Forum Newbie
Posts: 24
Joined: Wed Aug 03, 2005 10:47 am
Location: NW Louisiana

Post by DeprecatedDiva »

I'm completely inexperienced regarding security issues and still very new to using PHP. From what I've read so far, am I in better shape for using an array of approved includes? This is the method I read about and it is the one I am using. Should I be using a different method? (I am using a testbed server at home for the time being.) :roll: :?
User avatar
nielsene
DevNet Resident
Posts: 1834
Joined: Fri Aug 16, 2002 8:57 am
Location: Watertown, MA

Post by nielsene »

Yes, an explicit list of approved includes is a much more secure starting point. Its still possible to "mess it up" but its generally much safer.
User avatar
Jenk
DevNet Master
Posts: 3587
Joined: Mon Sep 19, 2005 6:24 am
Location: London

Post by Jenk »

Bit of forum Necromancy here, I sometimes use the following:

Code: Select all

<?php
$files = array('main', 'accounts', 'page2', 'blahblah', 'etc');

@include('/path/to/includes/' . $files[intval($_GET['pid'])] '.inc') or include('/path/to/includes/default.php');

?>
on my front controller(s) :)
Last edited by Jenk on Tue Oct 11, 2005 4:39 am, edited 1 time in total.
User avatar
Skara
Forum Regular
Posts: 703
Joined: Sat Mar 12, 2005 7:13 pm
Location: US

Post by Skara »

and that has to be the best way I've ever seen. ;)
Post Reply