fix(security): 附件 GET 端点防同源存储型 XSS——非白名单图片(svg/html等)强制下载+octet-stream+nosniff+CSP sandbox
maestro 双复审漏掉的真实 HIGH:上传文件与 API 同源,inline 渲染会在 console 源执行其脚本。只对 png/jpeg/gif/webp 允许 inline 预览,其余强制 attachment 下载并改 content-type 为 octet-stream,加 X-Content-Type-Options:nosniff + CSP sandbox 双重兜底。带 SVG 安全单测。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+9
-2
@@ -375,12 +375,19 @@ export function buildServer(opts: ApiOptions): FastifyInstance {
|
||||
|
||||
const dl = (req.query as { download?: string }).download;
|
||||
const fallbackName = att.name || name;
|
||||
// 安全:上传文件与本 API 同源,inline 渲染会在 console 源里执行其脚本 → 存储型 XSS。
|
||||
// 故只对【硬白名单图片类型】允许 inline 预览;其余(尤其 image/svg+xml、text/html 等可携带脚本的类型)
|
||||
// 一律强制 attachment 下载 + content-type 改 octet-stream,并加 nosniff + CSP sandbox 双重兜底。
|
||||
const SAFE_INLINE = new Set(['image/png', 'image/jpeg', 'image/gif', 'image/webp']);
|
||||
const inline = !dl && SAFE_INLINE.has(att.type ?? '');
|
||||
reply
|
||||
.header('cache-control', 'private, max-age=300')
|
||||
.header('content-length', String(st.size))
|
||||
.header('x-content-type-options', 'nosniff')
|
||||
.header('content-security-policy', "sandbox; default-src 'none'")
|
||||
.header('content-disposition',
|
||||
`${dl ? 'attachment' : 'inline'}; filename*=UTF-8''${encodeURIComponent(fallbackName)}`)
|
||||
.type(att.type || 'application/octet-stream');
|
||||
`${inline ? 'inline' : 'attachment'}; filename*=UTF-8''${encodeURIComponent(fallbackName)}`)
|
||||
.type(inline ? (att.type as string) : 'application/octet-stream');
|
||||
return reply.send(createReadStream(abs));
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user