Question: I have a base path /whatever/foo/

and
`$_GET['path']` should be relative to it.

However how do I accomplish this (reading the directory), without allowing directory traversal?

eg.

```
/\.\.|\.\./

```

Will not filter properly.

Answer: 
It is not sufficient to check for patterns like ../ or the likes. Take "../" for instance which URI encodes to "%2e%2e%2f". If your pattern check happens before a decode, you would miss this traversal attempt. There are some other tricks hackers can do to circumvent a pattern checker especially when using encoded strings.

I've had the most success stopping these by canonicalizing any path string to its absolute path using something like realpath() as ircmaxwell suggests. Only then do I begin checking for traversal attacks by matching them against a base path I've predefined.
I have written a function to check for traversal:

```
function isTraversal($basePath, $fileName)
{
    if (strpos(urldecode($fileName), '..') !== false)
        return true;
    $realBase = realpath($basePath);
    $userPath = $basePath.$fileName;
    $realUserPath = realpath($userPath);
    while ($realUserPath === false)
    {
        $userPath = dirname($userPath);
        $realUserPath = realpath($userPath);
    }
    return strpos($realUserPath, $realBase) !== 0;
}

```

This line alone `if (strpos(urldecode($fileName), '..') !== false)` should be enough to prevent traversal, however, there are many different ways hackers can traverse directories so its better to make sure the user starts with the real base path.

Just checking the user starts with the real base path is not enough because a hacker could traverse to the current directory and discover the directory structure.

The `while` allows the code to work when $fileName does not exist.