fix(clipboard): 修复SVG文件存储型XSS漏洞并完善文件名处理

1. 禁止SVG文件内联渲染,统一强制作为附件下载避免XSS
2. 修复fallback文件名未处理双引号导致的头信息格式破坏
3. 新增SVG XSS验证测试脚本
This commit is contained in:
yangxiangyuan
2026-08-12 13:11:15 +08:00
parent 6dc79a2c12
commit b21ef2ea3e
2 changed files with 120 additions and 4 deletions
+10 -4
View File
@@ -449,9 +449,12 @@ const createRouter = () => {
res.setHeader('Content-Type', mime)
res.setHeader('Cache-Control', 'private, no-store, max-age=0')
const safeName = normalizeName(row.file_name || 'file')
const isImage = String(row.type || '').toLowerCase() === 'image' || mime.toLowerCase().startsWith('image/')
const isSvg = mime.toLowerCase().includes('svg+xml')
// SVG 可在本站域名下执行脚本(存储型 XSS),一律强制下载,不允许 inline 渲染
const isImage = !isSvg && (String(row.type || '').toLowerCase() === 'image' || mime.toLowerCase().startsWith('image/'))
const dispositionType = isImage ? 'inline' : 'attachment'
const fallbackName = safeName.replace(/[^\x20-\x7E]/g, '_')
// fallback 文件名剔除不可打印字符与双引号,避免破坏 Content-Disposition 引号配对
const fallbackName = safeName.replace(/[^\x20-\x7E]/g, '_').replace(/"/g, '')
const utf8Name = encodeURIComponent(safeName)
res.setHeader('Content-Disposition', `${dispositionType}; filename="${fallbackName}"; filename*=UTF-8''${utf8Name}`)
return res.sendFile(abs)
@@ -799,9 +802,12 @@ const createPublicShareRouter = () => {
res.setHeader('Content-Type', mime)
res.setHeader('Cache-Control', 'private, no-store, max-age=0')
const safeName = normalizeName(row.file_name || 'file')
const isImage = String(row.type || '').toLowerCase() === 'image' || mime.toLowerCase().startsWith('image/')
const isSvg = mime.toLowerCase().includes('svg+xml')
// SVG 可在本站域名下执行脚本(存储型 XSS),一律强制下载,不允许 inline 渲染
const isImage = !isSvg && (String(row.type || '').toLowerCase() === 'image' || mime.toLowerCase().startsWith('image/'))
const dispositionType = isImage ? 'inline' : 'attachment'
const fallbackName = safeName.replace(/[^\x20-\x7E]/g, '_')
// fallback 文件名剔除不可打印字符与双引号,避免破坏 Content-Disposition 引号配对
const fallbackName = safeName.replace(/[^\x20-\x7E]/g, '_').replace(/"/g, '')
res.setHeader('Content-Disposition', `${dispositionType}; filename="${fallbackName}"; filename*=UTF-8''${encodeURIComponent(safeName)}`)
return res.sendFile(abs)
} catch (e) {