摘要
本文针对Web系统中存在的SVG文件上传安全风险,详细分析风险成因、安全加固踩坑问题,提供一套零代码、无业务影响的Apache层防御方案。通过全局开启MIME类型嗅探防护+精准对SVG资源禁用脚本执行策略,彻底解决SVG跨站脚本执行隐患,完美满足等保加固要求,适配生产环境直接落地。
⚠️ 免责声明:本文仅用于授权安全测试与防御技术研究,所有风险均已修复,严禁用于未授权攻击行为。
一、风险概述
业务系统支持图片资源上传功能,允许SVG格式文件上传。由于静态资源目录对外公开可直接访问,结合SVG本身支持内嵌事件解析的特性,在未做内容校验的前提下,存在脚本执行安全风险。
-
风险等级:中危
-
风险类型:跨站脚本风险(Web通用安全隐患)
二、系统架构环境
-
Web服务:Apache Httpd
-
后端服务:Jetty
-
静态资源目录:
/root/webui -
访问特性:资源路径公开,可通过URL直接访问,无访问鉴权
三、风险成因分析
1. 风险触发条件
该安全隐患成立需要满足三个条件,当前系统全部命中:
-
可上传恶意文件:系统允许上传SVG格式文件
-
文件落Web可访问目录 :上传后的SVG存放于站点静态目录,比如
/assets/下 -
浏览器可直接解析执行 :默认情况下浏览器会正常渲染SVG,并执行内部
onload/onclick等内嵌脚本
2. 核心根源
Apache默认站点目录资源全部公开访问,攻击者可通过拼接URL访问上传后的SVG资源。即使URL附带时间戳、随机参数,服务依旧正常解析资源,导致风险无法被拦截。
Apache Httpd默认配置下,DocumentRoot 目录下所有文件路径公开、可直接访问。
恶意SVG文件上传后,攻击者可直接通过如下格式URL触发XSS:
Plain
http://ip:port/assets/xxx.svg?xxx
3. 官方加固建议解读
安全加固给出两条建议,结合本系统做精准判断:
① 禁止存储桶URL拼接 response-* 参数
该规则仅针对阿里云/腾讯云OSS存储桶 ,Jetty、Apache均不识别此类参数,本地站点无此风险,无需改代码。
② 静态资源添加 X-Content-Type-Options: nosniff
通用强制加固项,用于防止浏览器MIME类型嗅探、文件类型混淆攻击。
四、场景复现
SVG是基于XML的图形格式,支持 onload、onclick 等原生事件。若系统未做内容过滤,攻击者可构造携带事件逻辑的SVG文件上传至服务器。
当用户访问该SVG资源链接时,浏览器会默认解析文件内容,触发内嵌事件逻辑,造成页面非预期执行风险。
本文不提供完整可利用POC,仅做安全原理分析与防御总结,避免技术被恶意滥用。
- 步骤1:制作SVG弹窗文件
新建文本文件,写入带原生弹窗脚本的SVG代码,将文件命名为xss.svg。该代码可正常被浏览器解析渲染,且加载时自动执行弹窗脚本,完整代码如下:
html
<svg xmlns="http://www.w3.org/2000/svg">
<animate attributeName="opacity" onbegin="window['al'+'ert']('XSS成功')" values="1" dur="1s"/>
</svg>
-
步骤2:上传svg文件
登录系统前端页面,进入图片/图标上传功能接口,选择上述制作的SVG文件,完成正常上传操作。系统未对SVG文件内容、内嵌脚本做过滤校验,文件可成功上传。
-
步骤3:确认文件存储路径
上传成功后,一般通过打开浏览器的Debug窗口,都能获取到系统返回文件静态资源的访问路径,比如上传的这个文件最终存储在Web站点公开静态目录:
/root/webui/assets/下,属于Apache可直接外网访问的站点资源目录。 -
步骤4:构造URL访问,触发XSS漏洞
根据返回的相对路径,拼接完整公网访问URL,格式如下:
http://ip:port/assets/xss.svg浏览器直接访问该地址,无需任何用户交互 ,页面自动弹出脚本弹窗,证明内嵌JS脚本执行成功,存储型XSS漏洞可被稳定触发。效果如下:

五、踩坑记录
本次加固过程尝试多种方案,总结大量生产踩坑点:
坑1:全局设置 attachment 强制下载
强制所有SVG下载会导致页面所有图标无法显示,业务直接瘫痪,不可使用。
坑2:全局配置 CSP script-src 'none'
全局禁止脚本会拦截全站JS,页面按钮、弹窗、交互全部失效,绝对不能全局配置。
坑3:仅配置 nosniff 无法修复风险
nosniff 只能防止类型嗅探,无法禁止SVG内部事件执行,风险依旧存在。
六、最终加固方案
-
全局开启
nosniff,满足官方安全加固要求,防止类型混淆攻击; -
仅对SVG文件单独配置CSP策略 :允许SVG作为图片正常渲染展示,但是禁止执行任何内嵌脚本;
-
不修改业务代码、不影响图标展示、不影响原有上传逻辑。
基于以上思路,可直接修改httpd的自定义配置文件,比如我这里是xss_test.conf。
xss_test.conf 大致配置
Plain
<VirtualHost *:8080>
DocumentRoot /root/webui
...
# 全局加固:禁止浏览器MIME类型嗅探(满足官方加固建议)
Header always set X-Content-Type-Options "nosniff"
# 专项防护:仅对SVG文件禁止脚本执行,彻底防XSS
<LocationMatch "^/assets/.*\.svg$">
Header always set Content-Security-Policy "script-src 'none'"
</LocationMatch>
...
</VirtualHost>
在实际环境中,配置内容类似下图所示:

七、配置参数详解
1. X-Content-Type-Options: nosniff
nosniff = no sniff(禁止嗅探)
作用:浏览器必须严格按照服务器返回的Content-Type解析文件,禁止自动猜测文件类型,杜绝文件类型混淆攻击。
2. Content-Security-Policy: script-src 'none'
对匹配的SVG文件禁止一切脚本、内联事件执行。
效果:恶意事件无法触发,但图片渲染完全正常,完美解决风险。
3. LocationMatch 匹配规则
^/assets/.*\.svg$ 匹配所有静态目录SVG资源,自动忽略URL参数,防护无死角。
八、修复效果验证
加固完成后,效果如下:
-
✅ 所有业务SVG图标正常展示,页面无错乱
-
✅ 恶意SVG内联事件被浏览器CSP策略强制拦截
-
✅ 控制台输出CSP拦截日志,风险彻底失效
如果已经修复,那么再次访问http://ip:port/assets/xss.svg,页面将不会弹窗,并且查看控制台,会有如下输出:

九、多种修复方案对比
除了上述的方案,还有其他的几种方案,但是,这几种方案都需要修改前端代码,所以不是很推荐。优缺点对比如下:
| 修复方案 | 优点 | 缺点 |
|---|---|---|
| Apache精准CSP防护(本文方案) | 零业务影响、无需改代码、防护精准 | 无短板 |
| 禁止SVG上传 | 彻底安全 | 影响页面图标展示 |
| 资源访问鉴权 | 安全性极高 | 需要改造业务代码 |
| 静态资源移出站点目录 | 无外网访问入口 | 改动量大 |
十、概念区分
简单说明一下, 存储型 和 存储桶:
-
存储型:漏洞类型名称,指恶意代码永久存储在服务器
-
存储桶:阿里云/腾讯云OSS对象存储文件夹,和本地服务器无关
本项目为本地Apache站点,无存储桶架构,第一条云存储加固规则无需处理。
十一、总结
本次SVG安全隐患的核心原因是:SVG支持事件解析 + 资源公开可访问 + 无脚本拦截策略。
通过Apache层配置全局nosniff + SVG专属CSP策略,可以零成本、零入侵、彻底修复跨站脚本风险,同时满足安全加固规范,是生产环境推荐解决方案。