用户给一个文件名,服务端怎么安全地把文件发出去?send_from_directory 保证了什么、没保证什么?文件名清洗会有什么副作用?这篇只验证标准库函数的行为(用一个目录外的哨兵文件确认没有泄漏)。
合法文件 a.txt → 200 'hello a' 子目录里的 sub/b.txt → 200 'hello b' 不存在的文件 → 404 '<!doctype html>\n<html lang=en>' 先进再出 sub/../a.txt → 200 'hello a' 跳到目录外 ../outside.txt → 404 '<!doctype html>\n<html lang=en>' 嵌在中间 sub/../../outside.txt → 404 '<!doctype html>\n<html lang=en>' 绝对路径形式 → 308 '<!doctype html>\n<html lang=en>' 含空字节 → 404 '<!doctype html>\n<html lang=en>' 目录本身 sub → 404 '<!doctype html>\n<html lang=en>' 目录外的哨兵内容有没有出现在任何响应里: False
上一篇:SQL Injection 与参数化查询;下一篇:文件上传类型校验。
本篇是防御向的验证:没有演示任何绕过方法,只确认标准函数会拒绝什么、会放行什么。
safe_join(基目录, 'a.txt' ) → <tmp>/pub/a.txt safe_join(基目录, 'sub/b.txt' ) → <tmp>/pub/sub/b.txt safe_join(基目录, 'sub/../a.txt' ) → <tmp>/pub/a.txt safe_join(基目录, '../outside.txt' ) → None safe_join(基目录, '/outside.txt' ) → None safe_join(基目录, 'sub/../../outside.txt' ) → None safe_join(基目录, '' ) → <tmp>/pub
访问 pub/link.txt(链接到目录外) → 200 'SENTINEL-OUTSIDE' 自己再检查 realpath 是否仍在基目录内: False (False 就该拒绝)
Content-Disposition: attachment; filename=" .txt"; filename*=UTF-8''%E4%B8%8B%E8%BD%BD%20%E7%9A%84%E6%96%87%E4%BB%B6.txt Content-Type: text/plain; charset=utf-8 | ETag: True | Last-Modified: True | Cache-Control: no-cache | Accept-Ranges: bytes Range: bytes=0-4 → 206 'hello' | Content-Range: bytes 0-4/7 If-None-Match → 304 中文名的文件 /files/报告.txt → 200 中文文件
secure_filename('report.pdf' ) → 'report.pdf'
secure_filename('my file (1).txt' ) → 'my_file_1.txt'
secure_filename('../x.txt' ) → 'x.txt'
secure_filename('a/b/c.txt' ) → 'a_b_c.txt'
secure_filename('报告.txt' ) → 'txt'
secure_filename('.hidden' ) → 'hidden'
secure_filename('' ) → ''
secure_filename('CON.txt' ) → 'CON.txt'
secure_filename('a.tar.gz' ) → 'a.tar.gz'