文件上传型 XSS 攻击链路全解析
文件上传功能是网站常见模块,但若未严格防护,可能成为存储型XSS攻击的入口。攻击链路如下:
1. 攻击原理
- 核心漏洞:未校验上传文件的内容类型和扩展名,导致恶意脚本文件(如伪装成图片的
.js或.html)被存储 - 触发条件:当用户访问包含恶意文件的页面时,浏览器自动执行脚本
- 危害范围:窃取 Cookie、会话劫持、钓鱼攻击等
2. 攻击链路(四步渗透)
graph LR
A[攻击者上传恶意文件] --> B[服务器存储文件]
B --> C[用户访问含恶意文件的页面]
C --> D[浏览器执行脚本→攻击达成]
步骤详解:
-
文件上传阶段
攻击者将恶意脚本伪装成正常文件(如virus.jpg实际为.html文件),内容包含 XSS 载荷:<img src=x onerror="alert('XSS');fetch('/steal?cookie='+document.cookie)"> -
服务器存储阶段
漏洞服务器未做安全校验:- 未验证 MIME 类型(如
image/jpeg实际为text/html) - 未过滤危险扩展名(如
.php,.html) - 未重命名文件(保留原始文件名)
- 未验证 MIME 类型(如
-
用户访问阶段
恶意文件通过以下方式被加载:- 直接文件链接:
https://site.com/uploads/virus.jpg - 页面嵌入:
<img src="/uploads/virus.jpg">
- 直接文件链接:
-
脚本执行阶段
浏览器解析文件时触发脚本,攻击生效:- 若为 HTML 文件:直接执行内嵌 JavaScript
- 若为图片文件:利用
onerror等事件触发 XSS
3. 防御措施(四层防护)
① 客户端校验
前端限制文件类型,但需配合服务端验证(易被绕过):
// 前端示例(非可靠防御)
document.getElementById('upload').addEventListener('change', (e) => {
const file = e.target.files[0];
if (!file.type.match('image.*')) alert("仅允许图片!");
});
② 服务端校验(关键防线)
# Python Flask 示例
from werkzeug.utils import secure_filename
import magic # 识别真实文件类型
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in {'png', 'jpg'}
@app.route('/upload', methods=['POST'])
def upload_file():
file = request.files['file']
if file and allowed_file(file.filename):
# 校验真实文件类型(非扩展名)
mime = magic.from_buffer(file.read(2048), mime=True)
if mime not in ['image/jpeg', 'image/png']:
return "非法文件类型!", 400
# 安全重命名并存储
filename = secure_filename(file.filename)
file.save(os.path.join('uploads', filename))
return "上传成功"
return "文件类型禁止", 403
③ 存储隔离
- 将上传文件存放到独立域名(如
static.example.com),利用浏览器的同源策略隔离脚本执行 - 设置 HTTP 头:
Content-Disposition: attachment强制下载而非执行
④ 输出编码
即使文件被访问,对输出内容进行编码:
<?php
// PHP 示例:输出时转义特殊字符
echo htmlspecialchars($file_content, ENT_QUOTES, 'UTF-8');
?>
4. 深度防护建议
- 文件内容扫描:使用杀毒引擎扫描上传文件
- 权限最小化:上传目录禁用脚本执行权限(如 Nginx 配置
location ~* \.(js|php)$ { deny all; }) - WAF 防护:配置 Web 应用防火墙规则拦截可疑载荷
总结:文件上传型 XSS 本质是 "信任漏洞链" 的崩塌。通过多级校验(类型/扩展名/内容)、存储隔离和输出编码,可切断攻击链路,将上传功能从"后门"变为"安全通道"。
846

被折叠的 条评论
为什么被折叠?



