如何安全地将任意深度的路径传递给web应用(在此案例中为Flask)?
我有一个表单,当提交这个表单时,它会把一个字符串发送到我的Flask应用。这个字符串是一个文件路径,所以我想确保它里面没有什么恶意的东西,比如 ../../../etc/passwd
。Flask使用的Werkzeug有一个很方便的函数叫 secure_filename
,可以把文件名中的恶意内容去掉。不过,问题是,当我给它一个完整的路径,比如 templates/example.html
,它会把 /
转换成 _
,结果变成了 templates_example.html
。
所以,我觉得把路径拆分成不同的层级是个好主意,这样我可以分别发送 templates
和 example.html
,然后在服务器上再把它们合在一起。这样做效果很好,但路径可能会很深。我可以把 dir1/dir2/dir3/dir4
直接拼在一起,希望没有人会再深入到 dir4
之下,但这样做似乎不太聪明。
那么,处理未知深度的路径验证的正确方法是什么呢?要用不同的方式验证吗?数据发送的方式要改吗?还是说先对路径进行编码,然后在服务器上解码?
2 个回答
在这种情况下,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
你可以使用 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
。