如何安全地将任意深度的路径传递给web应用(在此案例中为Flask)?

7 投票
2 回答
1783 浏览
提问于 2025-04-16 23:38

我有一个表单,当提交这个表单时,它会把一个字符串发送到我的Flask应用。这个字符串是一个文件路径,所以我想确保它里面没有什么恶意的东西,比如 ../../../etc/passwd。Flask使用的Werkzeug有一个很方便的函数叫 secure_filename,可以把文件名中的恶意内容去掉。不过,问题是,当我给它一个完整的路径,比如 templates/example.html,它会把 / 转换成 _,结果变成了 templates_example.html

所以,我觉得把路径拆分成不同的层级是个好主意,这样我可以分别发送 templatesexample.html,然后在服务器上再把它们合在一起。这样做效果很好,但路径可能会很深。我可以把 dir1/dir2/dir3/dir4 直接拼在一起,希望没有人会再深入到 dir4 之下,但这样做似乎不太聪明。

那么,处理未知深度的路径验证的正确方法是什么呢?要用不同的方式验证吗?数据发送的方式要改吗?还是说先对路径进行编码,然后在服务器上解码?

2 个回答

11

在这种情况下,Flask 提供了一个叫做 safe_join 的功能。如果用户试图离开指定的路径,它会返回一个404错误,表示找不到页面。

>>> safe_join('/foo/bar', 'test')
'/foo/bar/test'
>>> safe_join('/foo/bar', 'test/../other_test')
'/foo/bar/other_test'
>>> safe_join('/foo/bar', 'test/../../../etc/htpassw')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/mitsuhiko/Development/flask/flask/helpers.py", line 432, in safe_join
    raise NotFound()
werkzeug.exceptions.NotFound: 404: Not Found
2

你可以使用 werkzeug.routing.PathConverter 来处理任意路径,方法如下:

from flask import Flask
app = Flask(__name__)

@app.route("/arbitrary/<path:my_path>")
def arbitrary_path(my_path):
    return my_path

if __name__ == "__main__":
    app.run()

在上面的简单示例中,你可以看到,如果你访问 http://127.0.0.1:5000/arbitrary/dir1/dir2/dir3/dir4,它会返回 dir1/dir2/dir3/dir4。而如果你访问 http://127.0.0.1:5000/arbitrary/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9/dir10,它会返回 dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9/dir10

撰写回答