Menu

Reference / Http

safePath()

function
function safePath(string $root, string $reqPath): ?string

Maps a request path to a regular file under a document root, or returns null.

The root and the candidate both go through realpath, so .. segments and symlinks that leave the root are refused. A directory, a missing file, a missing root, an empty path, a path that does not start with / and any path ending in / all answer null: which index file a directory stands for is the handler's policy. Pass Request::$path, which the server has already percent-decoded and ..-collapsed.

$file = Http\safePath($root, $req->path);
return $file === null ? (new Response(404))->text('not found') : (new Response())->file($file);

Parameters

$root string
Directory to serve files from. It must exist.
$reqPath string
Request path, normally $request->path. It must start with /.

Returns

?string — The real path of a regular file inside $root, or null when there is none.

Example

// Http\safePath() turns a request path into a file path that cannot leave the root.

// A throw-away document root, with a secret file next to it.
$base = sys_get_temp_dir() . '/mc_doc_safe_path_' . getmypid();
$root = $base . '/public';
mkdir($root . '/css', 0777, true);
file_put_contents($root . '/index.html', '<h1>home</h1>');
file_put_contents($root . '/css/app.css', 'body{}');
file_put_contents($base . '/secret.txt', 'outside the root');
$real = realpath($root);

foreach (['/index.html', '/css/app.css', '/css/', '/css', '/missing.txt', '/../secret.txt', 'no-slash'] as $path) {
    $file = Http\safePath($root, $path);
    echo str_pad($path, 16), $file === null ? 'null' : substr($file, strlen($real)), "\n";
}

echo Http\mimeFor('/css/app.css'), "\n";
echo Http\mimeFor('photo.JPG'), "\n";
echo Http\mimeFor('archive.unknown'), "\n";

unlink($root . '/index.html');
unlink($root . '/css/app.css');
rmdir($root . '/css');
rmdir($root);
unlink($base . '/secret.txt');
rmdir($base);

Output

/index.html     /index.html
/css/app.css    /css/app.css
/css/           null
/css            null
/missing.txt    null
/../secret.txt  null
no-slash        null
text/css; charset=utf-8
image/jpeg
application/octet-stream

See also: Response

View source