phase 5 part 5: cs algorithms, network-os, web-security topics with tested examples; all 43 stacks pass --strict
This commit is contained in:
1 parent
f695bbd7cc
commit
691f738f5f
22 files changed
+1955
-37
No files matched your search
@@ -0,0 +1,77 @@
|
||||
---
|
||||
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。”
|
||||
Reference in new issue
Block a user