Is ASCII "../" the only sequence of bytes that indicates directory traversal in PHP?

I have a PHP application that uses the $_GET parameter to select JS / CSS files in the file system.

If I reject all requests in which the input string contains ./ , \ or bytes outside the visible 7-bit ASCII range, is it enough to prevent parent directories from cluttering when the path is passed to the underlying PHP (C based)?

I know zero byte vulnerabilities , but are there any other alternative / malformed character encodings that can creak with these checks

Here's the main idea (not production code):

 $f = $_GET['f']; // eg "path/to/file.js" // goal: select only unhidden CSS/JS files within DOC_ROOT if (! preg_match('@^[\x20-\x7E] +$@ ', $f) // outside visible ASCII || false !== strpos($f, "./") // has ./ || false !== strpos($f, "\\") // has \ || 0 === strpos(basename($f), ".") // .isHiddenFile || ! preg_match('@\\.(css|js) $i@ ', $f) // not JS/CSS || ! is_file($_SERVER['DOCUMENT_ROOT'] . '/' . $f)) { die(); } $content = file_get_contents($_SERVER['DOCUMENT_ROOT'] . '/' . $f); 

Update:. My question is really about how C file system functions interpret arbitrary ASCII sequences (for example, if there are undocumented escape sequences), but I understand that this probably depends on the system and may not be practicable.

My active check further requires that realpath($fullPath) start with realpath($_SERVER['DOCUMENT_ROOT']) , ensuring that the file is within DOC_ROOT, but the purpose of this post was to cut out realpath() (it turned out to be unreliable in various environments), while allowing unusual but valid URIs such as /~user/[my files]/file.plugin.js .

+4
source share
3 answers

You mentioned this yourself, but comparing the input realpath with a known root is the best solution I can think of. Realpath will allow any hidden path / file system functions, including symbolic links.

+1
source

When filtering input for security, always use whitelists, not backlists.

You must reject all paths that do not match /^([A-Za-z0-9_-]+\/?)*[A-Za-z0-9_-]+\.(js)|(css)?$/ .

This will allow normal segmented paths where each segment has letters, numbers, or _- .

+5
source

It may take a little rebuild, but even if you go ../../passwd , basename() will isolate it. Then you can put all the files you want to use in one folder.

Given ../../././././a/b/c/d.txt , basename($f) will be d.txt ; this approach seems wiser to me, instead of trying to outwit the user and forget about the hole.

+1
source

All Articles