tags:

views:

69

answers:

4
+4  Q: 

PHP secure root

My friend found a problem in my script, it gives acces to root files.

This url gives passwd file:

http://site.com/attachment.php?file=../../../../../../etc/passwd

How to escape this security hole?

+10  A: 

Dont download the files using URL String.... Define unique IDs to denote a file, rather than paths.

You might have seen downloads like this http://www.mysite.com/download.php?id=23423 what they do, use this id, to take out the file name and path from the db and then download it.

Starx
+1  A: 

I suppose you have a directory where all attachments are stored.

Just test if file is located in your directory.

 // http://www.php.net/manual/en/function.basename.php
 // http://cz.php.net/manual/en/function.file-exists.php 
 if (file_exists($attachments_path . "/" . basename($_GET['file'])) {
  // do work
 }

Starx posted a solution which seems fine. It can be done without a database, though. If somebody uploads a file you can store the file as md5($filename).$extension and use your script.

MartyIX
So if there's a file called "passwd" in the attachment directory, the code allows the download of /etc/passwd, too? I'm not a PHP programmer, but I think you should be more explicit in saying that the file to access should be formed in the same manner:$filename = $attachments_path . "/" . basename($_GET['file'])
Sami Koivu
+3  A: 

You can use realpath() and dirname() to check URLs against $_SERVER['DOCUMENT_ROOT'] (or whatever directory is "safe" for downloading).

If the result of realpath() points outside the safe directory, you can deny the download request.

There's also the open_basedir security directive (and runtime option as of 5.3).

banzaimonkey
+1  A: 

There are several different solutions. If there can be only a filename, a basename() solution would work.

However, if it can be path, a more complex solution is needed

//assume current directory, but can be set anything. Absolute path of course
$basedir   = dirname(__FILE__);
//assume our files are below document root. 
//Otherwise use it's root dir instead of DOCUMENT_ROOT
$filename  = realpath($_SERVER['DOCUMENT_ROOT'].$_GET['file']);
if (substr($systemdir,0,strlen($basedir)) !== $basedir) {
  header ("HTTP/1.0 403 Forbidden"); 
  exit; 
}

there is also a useful PHP configuration option open_basedir

Col. Shrapnel