文件上传漏洞指应用对上传文件校验不足,让攻击者上传并执行脚本(如 PHP webshell)、覆盖系统文件或造成存储滥用;路径遍历指应用拼接用户提供的路径未做校验,让攻击者用连续的 ../ 走出限制目录读取任意文件。两者都发生在“文件层”,一个负责进、一个负责读,是文件层攻击面的两大主力。
前面几节里的攻击多少要“绕”点什么,文件层这两个漏洞则几乎是“直给”:你开放上传,我就传个能执行的文件;你做文件下载,我就那点路径走个目录。检查不严的应用很容易被内网木马或一觉起来读走了配置。
设计用意是让用户传头像/附件,攻击者传的却是可执行脚本。只要服务器把用户上传的文件落在网站可对外访问的目录,文件名又未做处理,访问 上传目录/文件名.php 就能直接执行脚本。绕过手法包括改扩展名大小写、双扩展名、空字节、MIME 篡改——白名单校验必须绕过这几类,而不仅仅是“不许传 .php”。

文件下载接口若直接把用户参数拼成路径读取,如 download?file=report.pdf,路径改为 download?file=../../../../etc/passwd,就能一路穿过限制目录去读服务器任意文本。URL 编码(..%2f)、双重编码、特殊字符拼接都能绕朴素过滤,所以可靠做法是规范化后再限制在以应用根目录为基准的白名单范围内,而不是去拦 ..。
⚠️ 常见坑:只做“前端文件类型输入框限制”或“扩展名黑名单”。前端可选、黑名单可绕;真正有效的是服务端内容白名单 + 数据签名校验 + 目录隔离三层。
上传和路径遍历虽然分开讲,但真实攻击里常被串成一条链。举例:攻击者先上传一个带脚本或恶意的文件(如果上传校验就漏了),再借路径遍历或另一个下载接口把这个文件“读”出来——上传是放进去、遍历是取出来,两个洞一旦都开,等于你既允许陌生人往屋里扔东西,又给他一根能伸进屋子的杆子。所以排查文件层时别只看单向,要把“存的洞”和“读的洞”一并验:能传什么、能被谁读到、落点在不在可执行目录,三个问题一起回答,才叫完整的文件层盘点。
“用白名单”常被过度简化成一个口号,实际要白三个层面,缺一个都白搭。第一层是扩展名白名单,只放图片、文档这类业务需要的;第二层是内容白名单,用读取文件头校验数据源类型(SVG 里可能藏脚本、图片头后面跟着脚本内容都算异常);第三层是执行语义白名单,即上传目录本身要“禁执行”——就算前两层被绕,落到一个不执行的目录,脚本也跑不起来。真正的高分答案是三层都白,而不是只把扩展名列表写得长一点。这样即使某个环节瞒过校验,后续防线仍能拦住落地执行。
背景:一个上传头像与文件下载接口,均未做严格校验。操作一:上传一个改后缀的可执行脚本观察文件落点;操作二:下载接口填穿越路径。结果:脚本落在可访问目录且可被访问、穿越读取了配置文件。解读:上传缺类型白名单与脚本禁执行,下载缺路径规范化。变式:上传加内容签名扩展名白名单 + 随机改名 + 目录禁执行,下载做规范化并在白名单基目录内,重复两次操作均被拦下,两个洞同时封死。
文件层治理常只盯着“上传与下载两个接口”,容易漏掉文件在系统里的其它生命周期。更稳的做法是给文件做一张覆盖读写两侧的台账:写侧登记“谁上传、落哪个目录、扩展名与内容签名是什么、目录是否禁执行”;读侧登记“哪个接口能读、路径怎么拼、落点在不在允许基目录内”。把这张台账做成每次新加文件接口都要填的清单,就天然覆盖了第四条讲的“存与读连成链”的场景。更进一步,备份、导出、日志里的文件路径也要纳入排查——它们常常带着同样的拼接漏洞却没人注意,一旦被借道,攻击者一样能穿目录或拖文件。文件生命周期多长、存在哪台机器、谁备份、多久清理,问清楚这些,文件层的攻击面才不是两张孤立的接口表,而是一盘看得清的资产。
上传校验做在前(类型白名单、签名核对),但攻击者变着法子绕,落盘之后再说一次安全更稳。落盘后的“二次体检”可以包含:扫描落地文件内容是否含脚本特征或异常结构;确认落盘目录确实禁执行、文件名是随机值不可猜测;检查文件在后续的展示、下载、导出路径上不会被重新当作代码或可执行文件处理。这样即使“进门”那关被某种新奇绕过骗过,文件落地后还有一道“读侧”和“执行侧”的兜底,把“传了还能跑”的后半段掐断。它与前面那招台账结合,正好把上传/下载/执行的生命周期连成一个完整的窗口,不是只在进门那一下做安全。把这道二次体检固化成上传流程的默认一步,新加的文件接口也默认就有这一道,而不是等出了事再补。