Skip to content

01 · Web 应用安全:XSS / CSRF / SQL 注入 ​

前端天天在跟这三类漏洞的「前端侧」打交道,本篇给一个跨前后端的统一框架:它们为什么会发生、各自的判断口诀、以及服务端怎么兜底。读完你能在任何一次代码评审里快速定位「这里会不会出事」。

速览卡 ​

项内容
一句话定位XSS / CSRF / SQL 注入的根因、判断口诀与服务端统一防御,通用 + 服务端侧
核心内容三类漏洞的本质、各自的防御主手段、以及为什么「转义/过滤」都只是局部解
读完能回答这段用户输入能不能直接拼 HTML?SameSite 到底防什么?参数化为什么能根治注入?
相关板块A08 前端安全 | C16 服务端安全纵深 | N05 TLS 与证书

前端侧的具体做法(输入、DOM、第三方脚本)见 A08;本篇负责通用根因 + 服务端兜底。


一、先记三句口诀 ​

漏洞一句话根因判断口诀
XSS把「数据」当成了「代码」执行(HTML/JS)"这段内容里可能混进用户输入,且会被当 HTML 渲染吗?"
CSRF浏览器自动携带凭证,无法区分请求是谁发起的"这个请求是不是只靠 Cookie 验身份、且没有额外的防伪校验?"
SQL 注入把「数据」拼进了「SQL 语句」"这里是不是用字符串拼接拼出了 SQL,而不是参数绑定?"

共同点:信任了不该信任的输入,并且没有在"解释边界"上做正确的事。 防御的正确姿势几乎都不是"过滤坏东西",而是"明确区分数据与代码/指令"。


二、XSS:三个变体的共同本质 ​

2.1 三种形态 ​

类型触发点典型场景危害
存储型恶意内容先存到后端,别人访问时渲染评论、昵称、富文本最大:受害者是所有看到的人
反射型恶意内容在 URL 参数里,立即回显搜索框 ?q=<script>需诱导点击,但钓鱼链接防不胜防
DOM 型纯前端 innerHTML / eval 处理数据SPA 里拼接 HTML后端看不到,WAF 也拦不住

2.2 为什么「过滤 <>」靠不住 ​

过滤黑名单(<script>、onerror)总有绕过:编码变体、<img src=x onerror=...>、事件属性、javascript: 协议、SVG 里的脚本……

正确的分层防御:

层手段解决什么
输出编码按上下文编码(HTML/属性/JS/CSS/URL 各有各的编码)把数据当文本而不是代码
CSPContent-Security-Policy 限制脚本来源即使注入成功,也拦下执行
HttpOnly CookieJS 读不到会话令牌把 XSS 的伤害从「偷凭证」降级为「改页面」
输入校验白名单校验(邮箱、手机号格式)缩小可输入范围,但不替代上面三层

判断框架:默认不信任任何字符串当 HTML 用。 React/Vue/Flutter 的默认文本渲染是安全的;危险的是 dangerouslySetInnerHTML / v-html / innerHTML——用了就要想清楚数据来源与编码。


三、CSRF:问题出在「浏览器自动带凭证」 ​

3.1 攻击成立的两个前提 ​

text
1. 请求靠 Cookie(或基础认证)验身份 —— 浏览器会自动带上
2. 没有额外的防伪校验

攻击者只是让你在已登录的状态下访问一个页面,页面里的表单/图片就会带着你的 Cookie 发请求。

3.2 三个层次的防御 ​

手段做法覆盖度
SameSite CookieSameSite=Lax(默认)或 Strict拦掉跨站的自动携带,主防御
CSRF Token表单里带服务端签发的一次性 token,提交时校验拦掉"没有 token 也能过"的场景
同源校验 / 自定义头校验 Origin/Referer,或要求自定义请求头补充,尤其 API 场景

SameSite 的关键认知:它防的是跨站(site = 注册域),不是跨域。 同一域名下的子路径、同一站点下的不同子域属于同一 site,SameSite 拦不住——此时才需要 CSRF Token。

现代浏览器 Lax 已是默认,但仍建议显式声明,并配合 CSRF Token 做纵深。

3.3 前后端分别该做什么 ​

侧动作
前端登录/会话用 HttpOnly Cookie;写操作(POST/PUT/DELETE)不要用纯链接 GET 触发副作用
后端关键操作校验 CSRF Token;校验 Origin;对敏感操作二次确认(改密、支付)

四、SQL 注入:参数化是根治,不是选项 ​

4.1 本质 ​

python
# ❌ 拼接:用户输入变成 SQL 的一部分
sql = "SELECT * FROM users WHERE name = '" + name + "'"

# ✅ 参数化:数据与语句分离,数据库引擎区分对待
sql = "SELECT * FROM users WHERE name = %s"

拼接把「数据」混进「语句」;参数化让数据库知道哪些是值、哪些是结构——这正是与 XSS 完全同构的解法(数据与代码分离)。

4.2 为什么「过滤 '」「WAF」都不是根治 ​

  • 过滤单引号:编码、注释符 --、\ 转义都有绕过。
  • WAF:能挡常规攻击,但挡不住业务逻辑里新出现的拼接点,且会产生误杀。
  • ORM 自动转义:只要 ORM 的参数化查询路径被正确使用就是安全的;但原生 SQL / 动态拼接依然危险。

4.3 服务端兜底清单 ​

手段说明
参数化查询 / ORM 绑定唯一根治手段,默认使用
最小权限的 DB 账号应用账号只给需要的库/表权限,注入拿不到管理员权限
输入校验类型/长度/格式白名单,减少攻击面(但不替代参数化)
错误信息不外泄不要回显 SQL 报错细节给客户端

注入不只是 SQL:命令注入(拼 shell)、模板注入(SSTI)、路径穿越是同一思想在不同解释器上的重演。看到一个「字符串拼进另一个语言」的地方,就要警觉。


五、服务端统一防御:一张检查表 ​

检查项默认动作
任何用户输入进 SQL/Shell/模板参数化 / 明确转义,不拼接
任何用户内容进 HTML/属性/JS按上下文编码;禁止裸 innerHTML
会话凭证HttpOnly + Secure + SameSite Cookie;不用 localStorage 存 token
写操作CSRF Token + 同源校验;敏感操作二次确认
错误返回统一错误体,不吐堆栈/不吐 SQL
权限接口层校验 + 数据层过滤(防 IDOR)
依赖锁文件 + 定期漏洞扫描(SCA,见 S07)
传输全站 HTTPS + HSTS(见 N05)

这套检查表可以固化成代码评审 checklist 与 CI 里的安全扫描,比靠个人记性可靠得多。


六、自检清单 ​

  • [ ] 能说清三类漏洞各自「信任了什么不该信任的输入」
  • [ ] XSS 知道按上下文编码,且知道 <> 过滤不可靠
  • [ ] 知道 CSP、HttpOnly 各解决 XSS 的哪一部分
  • [ ] 知道 SameSite 防的是跨站,不是跨域;何时仍需要 CSRF Token
  • [ ] SQL 一律参数化,且知道「过滤单引号」为什么不够
  • [ ] 错误信息不外泄,应用 DB 账号最小权限
  • [ ] 见过「字符串拼进另一种语言」的场景都能拉响警报

七、常见误区 ​

误区事实
"过滤了 <script> 就安全"黑名单总有绕过;按上下文编码 + CSP 才是正解
"XSS 只在前端"存储型把恶意内容存后端,所有访问者都会中招
"用了 HTTPS 就不怕 CSRF"HTTPS 防窃听,防不了「已登录会话被利用」
"SameSite=Lax 就万事大吉"同站子域、GET 触发副作用、旧浏览器都可能绕过
"过滤单引号能防 SQL 注入"编码与注释绕过多;参数化才是根治
"ORM 一定安全"ORM 的参数化路径安全,原生 SQL/动态拼接依然危险
"输入校验就能防注入"校验减少攻击面,但不能替代数据与代码分离
"报错信息给用户看方便调试"泄露表结构/路径/依赖信息,是攻击者的地图

八、动手练习 ​

练习 A:给一段渲染做"安全体检" ​

jsx
// 这三行哪几行有风险?分别是什么类型的 XSS?怎么修?
<div>{user.nickname}</div>
<div dangerouslySetInnerHTML={{ __html: article.content }} />
<img src={user.avatarUrl} />

练习 B:追一次注入 ​

给你一个搜索接口 GET /search?q=...,后端用字符串拼接拼出 SQL。说出三种修复方式,并说明哪种是根治。

练习 C:设计一次"改绑手机号" ​

要求:已登录、HTTPS、防 CSRF、防撞库。列出你会加的全部防护,并说明每一层挡住的是什么攻击。


相关文档 ​

本区 ​

其他域 ​