[web-devel] How do we really know if a path is safe when serving files?
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.
On Mon, Jun 27, 2011 at 2:53 PM, Jeremy Shaw <jeremy at n-heptane.com> wrote:
> 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:
> 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?
> - jeremy
> web-devel mailing list
> web-devel at haskell.org
-------------- next part --------------
An HTML attachment was scrubbed...
More information about the web-devel