我见过一个最蠢的文件漏洞,不是名漏代码多高深,而是文件文件名里藏了个空格。某甲方系统允许用户上传附件,名漏后台校验只查了扩展名是文件不是jpg。有人传了个"shell.php "——注意,名漏php后面跟了个空格。文件Windows系统在保存文件时,名漏这个空格会被自动去掉,文件于是名漏服务器上就多了一个完整的PHP脚本。上传者随后直接访问这个文件,文件拿到了服务器权限。名漏整个攻击过程不到十秒。文件
这种问题根源在于,名漏开发者用一套规则处理文件,文件操作系统却用另一套规则解释文件。你校验的是文件名,系统执行的是文件内容。两边一错位,漏洞就出来了。
更常见的场景是路径穿越。文件名里带"../",上传接口没过滤,用户就能把文件写到任意目录。我见过一个案例,攻击者把webshell写到网站的静态资源目录,然后通过CDN节点直接访问。管理员查日志发现访问来源全是CDN的IP,追踪了半天没找到人。最后发现是文件名里用了URL编码的"..%2f",服务器解码后路径就变了。
还有一种隐蔽的玩法,利用Windows的保留设备名。文件名叫"CON"、"AUX"、"NUL"这些,在Windows系统里根本创建不了。但如果上传接口跑在Linux上,后端存储用的是Linux,前端下载时却用Windows服务器做代理,攻击者上传一个名为"CON"的文件,下载时Windows就会报错,甚至可能触发其他逻辑错误。
有人会说,那我把文件名全部重命名成随机字符串,不就行了?大部分情况确实能堵住漏洞。但有些业务场景必须保留原始文件名,比如网盘分享、文档预览。这时候就得靠白名单过滤——只允许字母、数字、下划线、连字符和单个点。可很多开发者图省事,用黑名单过滤,把"php"、"asp"、"jsp"这些后缀拉黑。结果攻击者用"phtml"、"php3"、"pht"这些冷门后缀,照样能解析执行。
我印象最深的一次排查,是某个内部系统上传头像功能。所有常规测试都过了,后缀过滤、路径检查、MIME校验全做了。但某天突然发现服务器上多了一个"avatar.php.ico"文件——这是利用了Apache的解析特性,从右往左认后缀,遇到不认识的就跳过,最后用php来解析。开发者只拦了最后的后缀,没检查整个文件名。
说到底,文件名漏洞的核心矛盾是:用户输入的文件名,到底该由谁来信任?你信任了,就出问题;你不信任,就得做全面重写。但重写文件名又可能破坏业务逻辑。所以最稳妥的方案是——存储层用随机UUID做文件名,展示层把原始文件名单独存数据库,下载时通过接口映射。双重分离,互不干扰。
这个行业里,几乎所有搞安全的都知道这个道理,但实际开发中,总有人为了省事,直接拿用户输入的文件名去拼接路径。那些出事的系统,往往不是技术不行,而是懒惰。