views:

240

answers:

3

Does anyone know a PHP RegEx to allow only relative paths, not absolute?

For example, I have an AJAX function that submits this value to my PHP script "some-directory/another-directory/some-file.php".

My PHP script then includes that file.... include($some-php-document);

I don't want a hacker to be able to use my AJAX function to submit something like: "http://www.malicious-website.com/malicious-script.php"

In my PHP Document I would like to do something like:

<php>
$some-php-document = $_POST["some_value_submitted_via_ajax"];

//if $some-php-document is a URL (not a relative path to a file), 
    //then $some_php_document = null

//how can I use a php regex to accomplish the above?

</php>

How can I do this?

Or, let me know if there are more secure solutions.

A: 

You could test for :// in the string and if it's present, reject. If you want a negative regex, try something like:

^([^:]|:[^/]|:/[^/})*$

If it matches, the string is relative (although possibly malformed).

Blindy
Protocol is not required in a URL. //otherserver.com/script.js is valid.
Peter Boughton
Are you sure? I just input that address in my browser and it auto-filled file://
Blindy
+3  A: 

Don't allow the upload of file names to specify an include. Instead do this:

$valid_files = array(
    'file1' => 'file1.inc',
    'file2' => 'john.inc',
    'fred' => 'bob.inc',
);

$requested_include = trim(strtolower($_POST["some_value_submitted_via_ajax"])));
if (!array_key_exists($requested_include, $valid_files)) {
    $requested_include = "file1";
}
include WEB_ROOT_PATH . 'templates/whatever/' . $valid_files[$requested_include];

Validate all input, no need for a RegEx - it's hard to get right and you're better off with a tighter security solution as above.

razzed
+2  A: 

I would use the parse_url() function rather than a regexp, something like:

<?php
$some_php_document = $_POST["some_value_submitted_via_ajax"];

$parsed_doc = parse_url($some_php_document);

//if $some-php-document is a URL (not a relative path to a file), 
    //then $some_php_document = null
if (isset($parsed_doc['scheme']) || isset($parsed_doc['host'])) {
    $some_php_document = null;
}

?>

The file path will be in $parsed_doc['path']. It should be checked before being used, so an attacker can't say, request /etc/passwd or some similarly sensitive file.

Ken Keenan
The program users need to be able to specify any path, so I don't know a good way to check. What I did instead was use your example and wrap the include statement in ob_start() and ob_end clean(). That way, if a hacker includes a sensitive file that echoes content, the echoed content will be destroyed. Please let me know if you have a better idea.