[web-devel] How do we really know if a path is safe when serving files?

Greg Weber greg at gregweber.info
Tue Jun 28 00:15:08 CEST 2011


In wai-app-static we also filter out files beginning with just a single '.'
(assuming they are supposed to be hidden files).

The WAI protocol already breaks up a request into pieces. When you switch
over to WAI we would love to combine forces with you on wai-app-static. We
now have caching headers, embedded file support, and a test suite. We have
already stolen a few things from the Happstack file serving code.

Greg Weber

On Mon, Jun 27, 2011 at 2:53 PM, Jeremy Shaw <jeremy at n-heptane.com> wrote:

> Hello,
>
> Most frameworks provide some sort of mechanism for serving files from
> a directory. Generally some subset of the url is mapped directly to
> files on the disk. But, with this comes the risk that users will be
> able to access files they should not be able to. For example,
> something like:
>
> http://localhost:8000/../../../etc/passwd
>
> The tricky question is, how do we determine what paths are safe and
> what paths are not.
>
> I think the first step is to break the url into path segments and url
> decode each segment. So something a bit like this:
>
> pathElements :: String -> [String]
> pathElements = map urlDecode .  splitOnChar '/'
>
> note that we want to split on '/' before we decode. That is because
> the url might contain an embedded %2f, which is the '/' character. It
> is encoded as %2f precisely because it is *not* supposed to treated as
> a path separator.
>
> Now that we have a list of path segments we want to check that those
> path segments can actually be mapped to a valid and safe path on the
> disk. So, we need to filter out any path segments that contain
> embedded path separators (like / or \ depending on the platform).
> Those characters can appear in a valid url. But they can never appear
> in a valid path segment (they can only be used as path separators).
>
> We also want to reject any path that contains embedded "..". Or do we?
> In theory, we could canonicalize  path, "foo/../bar" to just "bar"?
> Certainly *after* canonicalization, we want to reject any path that
> contains a ".." segment. Under windows we have some extra concerns.
> For example, there are special names like LPT1, COM1, etc. Also, we do
> not want to allow someone to specify a drive name like C:\foo.
>
> But, how do we know if we have gotten everything right ?
>
> An alternative method would be to use
> System.Directory.canonicalizePath on the root directory we are serving
> from and the requested file. Then we check that the canonicalized root
> directory is the prefix of the canonicalized requested path:
>
> isChild :: FilePath -> FilePath -> IO Bool
> isChild root requested =
>  do root' <- canonicalizePath root
>       requested' <- canonicalizePath requested
>       return (root' `isPrefixOf` requested')
>
> (Needs to handle exceptions when the root or requested path does not
> actually exist).
>
> One issue with that solution is that it disallows the use of symlinks
> which point to directories outside of the root. That can be viewed as
> either a feature or a bug. It is also not clear that it is actually
> free of any security issues.
>
> So, this is what I have so far. First I use a function like the above
> 'pathElements' to split the url into path segments.
>
> Then I use this function to test that it is a safe/valid path:
>
> isSafePath :: [FilePath] -> Bool
> isSafePath [] = True
> isSafePath (s:ss) =
>     isValid s
>  && (all (not . isPathSeparator) s)
>  && not (hasDrive s)
>  && not (isParent s)
>  && isSafePath ss
>
> -- note: could be different on other OSs
> isParent :: FilePath -> Bool
> isParent ".." = True
> isParent _    = False
>
> Something like this:
>
> let pathEls = pathElements requestPath
> in if (isSafePath pathEls)
>    then let fp = joinPath (rootPath : pathEls) in ....
>    else fail "unsafe path"
>
> Does anyone see any issues with this? Security bugs or otherwise?
>
> Thanks!
> - jeremy
>
> _______________________________________________
> web-devel mailing list
> web-devel at haskell.org
> http://www.haskell.org/mailman/listinfo/web-devel
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.haskell.org/pipermail/web-devel/attachments/20110627/53826676/attachment.htm>


More information about the web-devel mailing list