如何从Python代码中防止目录遍历攻击

8

我需要使用Python防止目录遍历攻击。以下是我的代码:

if request.GET.get('param') is not None and request.GET.get('param') != '':
    param = request.GET.get('param')
    startdir = os.path.abspath(os.curdir)
    requested_path = os.path.relpath(param, startdir)
    requested_path = os.path.abspath(requested_path)
    print(requested_path)
    tfile = open(requested_path, 'rb')
    return HttpResponse(content=tfile, content_type="text/plain")

这里需要用户运行类似于 http://127.0.0.1:8000/createfile/?param=../../../../../../../../etc/passwd 这样的内容,同时需要防止目录遍历攻击。


用户不应被允许访问或修改sudo目录。因此,请检查路径是否为sudo目录。 - Arpit Solanki
我也需要防止那种情况发生。你可以这样做吗? - satya
不是说这是一个好的解决方案,但大约有20个根目录,因此请检查路径不包含其中任何一个,例如如果用户请求包含bin目录的路径,则不允许。 - Arpit Solanki
3个回答

12

假设用户的所有内容都位于

safe_dir = '/home/saya/server/content/'

/结尾很重要,正如heinrichj所提到的那样,以确保以下检查与特定目录匹配。

您需要验证最终请求是否在其中:

if os.path.commonprefix((os.path.realpath(requested_path),safe_dir)) != safe_dir: 
    #Bad user!
如果允许请求路径为save_dir本身,则还需要在os.path.realpath(requested_path)+'/' == safe_dir的情况下允许进入。我建议您确保所有想要用户访问的东西都放在一个地方。

2
你应该考虑添加 os.path.realpath(),以防安全路径中存在指向其外部的符号链接。 - zwer
@satya 不可以,因为你可能从其他地方执行。如果Python文件在“安全”目录中,请使用safe_dir=os.path.realpath(__FILE__),虽然我通常将内容与代码分开。还要注意,应该使用realpath,正如zwer所提到的那样。 - kabanus
“safe_dir” 给出的是 /opt/lampp/htdocs/Nuclear_reactor/d50/nuclear_correct/__FILE__,这样对吗? - satya
@satya 我打错了 - 忘记获取目录和变量名:safe_dir=os.path.dirname(os.path.realpath(__file__)) - kabanus
让我们在聊天中继续这个讨论。 - satya
显示剩余7条评论

9
你可以尝试使用 pathlib.Path 的方法。
Path(root_dir).joinpath(param).resolve().relative_to(root_dir.resolve())

应该返回相对路径,从root_dir开始,如果尝试进行目录遍历攻击,则引发ValueError异常。

测试

param = 'test_file'
Path(root_dir).joinpath(param).relative_to(root_dir)

WindowsPath('test_file')

param = 'test_file/nested'
Path(root_dir).joinpath(param).relative_to(root_dir)

WindowsPath('test_file/nested')

param = 'non_existing/../../data'
Path(root_dir).joinpath(param).resolve().relative_to(root_dir.resolve())
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-26-a74379fe1817> in <module>()
....
ValueError: 'C:\\python_scripts\\PyCharmProjects\\data' does not start with 'C:\\python_scripts\\PyCharmProjects\\testproject'
param = 'non_existing/../nested'
Path(root_dir).joinpath(param).resolve().relative_to(root_dir.resolve())

WindowsPath('nested')

WindowsPath('嵌套')


-1
下面这样的检查也可以防止遍历。
if '..' in pathParam:
    abort(ERRORCODE)

这是可以轻易绕过的,参见https://owasp.org/www-community/attacks/Path_Traversal - Stypox

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接