Files

78 lines
4.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: cs.web-security.scenario.ai-app-security
title: AI 客服上线前的安全评审:回答渲染 XSS、抓取工具 SSRF、Text-to-SQL 注入和跨域配置
type: scenario
domain: cs
stack: [web-security, fastapi, vue, function-calling]
tags: [安全评审, XSS, SSRF, Text-to-SQL, CORS, CSP, 最小权限]
level: intermediate
prerequisites: [cs.web-security.common-attacks]
est_hours: 2.5
related: [cs.web-security.interview, ai.function-calling.scenario.safe-business-tools, ai.prompt-engineering.scenario.injection-defense]
week: [14, 16]
---
# AI 客服上线前的安全评审:回答渲染 XSS、抓取工具 SSRF、Text-to-SQL 注入和跨域配置
## 业务背景与问题
AI 客服即将对外上线,安全团队评审发现:
1. 回答用 `v-html` 渲染 Markdown 转换后的 HTML,知识库中的 HTML 片段会原样执行;
2. Agent 有“打开链接查看内容”的工具,测试人员让它访问 `http://169.254.169.254/` 拿到了云主机元数据;
3. 报表功能让模型生成 SQL 直接执行,账号有写权限;
4. 后端 CORS 配置为允许所有来源并允许凭证。
## 约束
- 一周内上线,不能大改架构。
- 功能不能砍。
## 方案设计
1. **回答渲染(XSS)**:Markdown 渲染时禁用原始 HTML;对生成的 HTML 用白名单清洗(只保留段落、列表、代码、链接等);链接只允许 `http(s)`,外链加 `rel="noopener noreferrer"`;图片只允许白名单域名(防止通过图片 URL 外带数据);全站 CSP 禁止内联脚本;会话 Cookie HttpOnly。
2. **抓取工具(SSRF)**:协议白名单;解析域名后所有 IP 必须是公网;禁止自动重定向(或每一跳重新校验);连接时使用已校验的 IP,防止 DNS 重绑定;工具运行在只能访问公网的出口代理后面(网络层兜底);响应大小和超时限制。
3. **Text-to-SQL**:只读数据库账号,且只授权报表视图;SQL 先解析,只允许单条 `SELECT`、白名单表、强制 `LIMIT` 和租户条件;执行超时;记录审计日志。
4. **CORS**:白名单为自己的前端域名;同时启用 CSRF 防护(SameSite + Token)。
5. **验证**:把每个问题写成自动化安全测试(攻击载荷 → 预期被拒),加入 CI。
## 关键代码
回答渲染(前端,示意):
```ts
import DOMPurify from "dompurify";
import MarkdownIt from "markdown-it";
const md = new MarkdownIt({ html: false, linkify: true }); // 不允许原始 HTML
export function renderAnswer(text: string): string {
return DOMPurify.sanitize(md.render(text), {
ALLOWED_TAGS: ["p", "ul", "ol", "li", "code", "pre", "strong", "em", "a", "blockquote", "table", "thead", "tbody", "tr", "th", "td"],
ALLOWED_ATTR: ["href"],
ALLOWED_URI_REGEXP: /^https?:\/\//i, // 只允许 http(s) 链接
});
}
```
SSRF 校验与 SQL 注入、CORS、CSRF 的可运行实现和攻击测试:[examples/secure-api](../examples/secure-api/)。
## 取舍
| 选择 | 优点 | 缺点 |
| --- | --- | --- |
| **应用层校验 + 网络层出口限制(本方案)** | 纵深防御,一层失效还有另一层 | 实施工作量较大 |
| 只做应用层校验 | 快 | 校验有遗漏(重定向、DNS 重绑定)时没有兜底 |
| 去掉抓取工具 / 报表功能 | 最安全 | 业务不接受 |
## 易错点
- 认为“模型输出是我们自己生成的”就可以信任。实际上它会复述用户输入和知识库内容。
- SSRF 只拦截了 `127.0.0.1`,没拦 `localhost`、IPv6 `::1`、十进制 IP、内网域名。
- Text-to-SQL 用正则判断是否为 SELECT,被注释和多语句绕过。
- 只修复了被发现的那一个载荷,没有写成测试。
## 面试怎么讲
> “上线前安全评审发现四个问题,我按纵深防御处理:回答渲染禁用原始 HTML、白名单清洗、只允许 http 链接,加 CSP 和 HttpOnly;抓取工具解析域名后检查所有 IP 必须是公网、禁止重定向,并放在只能访问公网的出口代理后面;Text-to-SQL 换成只读账号和报表视图,SQL 解析后只允许单条 SELECT 并强制 LIMIT 和租户条件;CORS 改成白名单并加 CSRF Token。每个问题都写成了攻击测试放进 CI。”