Reputation: 229521
Part of my app requires the client to request files. Now, a well-behaved client will only request files that are safe to give, but I don't want a user to go about supplying "../../../creditCardInfo.xls"
, instead. What's the best practice for/simplest way to secure a filename to make sure that no files are served that would be higher than a certain point in the directory hierarchy? First instinct is to disallow filenames with ..
in them but that seems... incomplete and unsatisfactory.
The current questions about filename safety on SO focus on making a writable/readable filename, not ensuring that files that shouldn't be accessed are accessed.
Upvotes: 2
Views: 1578
Reputation: 27022
Here's the approach I use, which I think has the benefits of giving easy control over access to the files, and preventing path manipulation.
When the user uploads the file:
To get the file:
Upvotes: 0
Reputation: 52040
I think you are looking for a way to find the canonical(*) path of a file. That is with ..
, .
and symbolic links removed. That is the role of os.path.realpath
>>> os.path.realpath(".")
'/home/sylvain'
>>> os.path.realpath("..")
'/home'
>>> os.path.realpath("./Documents/../..")
'/home'
realpath
will follow symlink and "reduce" the path as well:
sylvain@daal:~$ ln -s /etc/password z
sylvain@daal:~$ python
Python 2.6.6 (r266:84292, Dec 26 2010, 22:31:48)
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import os.path
>>> os.path.realpath("z")
'/etc/password'
>>> os.path.realpath("z/..")
'/etc'
>>> os.path.realpath("./Documents/../z/..")
'/etc'
... whereas normpath
is easily abused:
>>> os.path.normpath("./Documents/../z/..")
'.'
Once you have the canonical name, you could easily check if the user should have access to the requested file. Say by comparing to a white-list.
(*) A file may have different path but only one canonical path.
See http://www.xyzws.com/Javafaq/what-is-the-difference-between-absolute-relative-and-canonical-path-of-file-or-directory/60 for more info.
Upvotes: 0
Reputation: 43590
If you're running in a UNIX variant, you might want a chroot jail to prevent access to the system outside your application.
This approach would avoid you having to write your own code to deal with the problem and let you handle it with infrastructure setup. It might not be appropriate if you need to restrict access to some area within the application as it changes what the process thinks is the system root.
Upvotes: 1
Reputation: 229521
This seems like it would work, provided that open
uses the same mechanism to resolve paths as os.path.abspath
. Are there any flaws to this approach?
import os
def is_safe(filename):
here = os.path.abspath(".")
there = os.path.abspath(filename)
return there.startswith(here)
>>> is_safe("foo.txt")
True
>>> is_safe("foo/bar/baz")
True
>>> is_safe("../../goodies")
False
>>> is_safe("/hax")
False
Upvotes: 1