上传功能出事,多半不是代码写得烂,是校验只做在了前端那一层。浏览器里点提交,进度条走完,看着挺顺,可后端接过来的东西到底是张图还是一个能执行的脚本,没人问过。文件上传漏洞防御听着像门大工程,落到实处就是几道关卡,而这几个关卡里最常漏的三处,这些年我见得太多了。

文件上传漏洞防御第一处漏点:后缀名这道关
后端判断文件类型,很多人的第一反应是写黑名单:php、jsp、asp、exe 摆一排。省事,但漏得快。你把 php 拦了,.php5、.phtml、.pht、.jspx 怎么办,大小写混着写呢,.PhP。再狠一点,双后缀 a.php.jpg,有些老系统取后缀取的是第一个点后面那段。还有截断那套,%00 塞在文件名里,某些年头的解析逻辑就当没看见后面那些字符。
这些是老掉牙的招,扫站脚本每年还在用,说明还有站没堵上。靠谱点的是白名单:只放 jpg、png、gif、pdf、zip 这类业务明确要用的,其余一律拒。取后缀取最后一段,别用 split 之后拿第一个。更要紧的一步是上传完成后改名,别让用户传上来的名字直接落盘。名字一换,目录一隔离,就算传了个怪东西也调不起来。
还有个常被忽略的地方:上传入口不止一个。主站的表单改好了,编辑器插图、头像裁剪、附件组件、导入 excel 的接口,各自实现一套,谁也不知道谁用的什么过滤。这些地方往往还带着默认配置,直接开着的。
Content-Type 这东西客户端能自己填
有人觉得后端校验了 MIME,看到 image/jpeg 就放行,这就安全了。抓个包就明白,那行是请求头里自己写的,改一个字母的事。前端的 accept=”image/*” 更只是装饰,删掉照样能传上去。
所以 MIME 顶多算辅助,得跟文件内容对得上才有参考价值。真正的判定要靠读文件本身:图片用 getimagesize 或者对应的图像库解一遍,PDF 看文件头结构,压缩包试着打开列一下条目。做一次真实解析,比看十个请求头都强。
图片马和二次渲染:内容校验别只看头几个字节
图片马上传这个概念很多人听过没细想:把一个脚本追加在正常 jpg 的尾巴上,前面几个字节还是合法图片头,只比对魔数的检测就穿过去了。要是站点另外还有本地文件包含的毛病,这张“图片”就能被当成代码跑。
抗这个的常规做法是二次渲染,图片上传后重新生成一遍,缩略图、转码都算,把多余数据丢掉。代价是动图会掉帧、颜色可能偏一点,看业务能不能接受。不能重渲染的类型,比如 PDF、压缩包、办公文档,就换个思路:存到不对外解析的地方,下载走鉴权,别给直链。
上传目录能执行脚本,前面几道关都白搭
这个漏点最冤。代码里查得好好的,后缀白名单、内容也验了,可上传目录就放在网站根下,服务器默认按扩展名解析。一份“合法”文件被塞了东西,访问那个路径就执行了。这不是校验的问题,是部署的问题。
做法上,上传目录独立出来放到 web 根之外,要访问就走一个带鉴权的读取脚本;实在要放根下,就在 nginx 的 location 里关掉脚本解析,Apache 里用 RemoveHandler、RemoveType 把 php 之类摘掉。顺手把权限分清,上传目录只写不执行,配置目录只读不写。宝塔面板这类工具里也有对应开关,叫法不一样意思一样,具体项以面板或官方文档说明为准。
顺带说一句,WAF 能挡一部分自动化扫描,但它不是答案。WAF 放过去的正常请求,最后还是要落到你的代码和目录配置上。
文件名、路径、大小这几处小地方
文件名里塞 ../ 想往上跳一层,是老招。拼接路径之前把文件名规范化,去掉分隔符和特殊字符,只留白名单字符,比自己写一堆过滤规则省心得多。
上传大小要限,不光是省带宽。一个几百兆的压缩包解开能撑爆磁盘,解压前先看条目数和展开后的总体积,多个文件一次传的话,总大小和个数也得有上限。允许 html、svg 这类能带脚本的类型要格外小心,同源下打开就等于给了对方一个执行位,要么不放行,要么强制走下载头。
落地顺序:先改哪个后改哪个
不用一次改完,按风险排就行。先确认上传目录不在可执行的位置,再换成后缀白名单加落盘改名,然后补内容解析,最后才是权限和日志这些收尾的活。日志别省,记下谁传了什么、从哪个 IP、多大,出事的时候能顺着查。
心态这块也提一句:别指望一个过滤函数守住整条路。从表单到落盘到前端展示,每一环都可能出岔子。写代码的时候把用户传上来的东西一律当脏的看,大部分坑就绕过去了。
回头翻翻自己站上的上传入口,主表单、头像、附件、导入的表格,还有那些忘了删的测试接口。挑最旧的那个先看,十有八九能翻出点东西。查完把清单记下来,下次改版还得用得上。
【版权声明】:本站内容若无意侵犯到您的权利,请及时与我们联系将尽快删除相关内容!