7AA7 博客

常见 Web 漏洞科普:SQL 注入、XSS、CSRF 是怎么回事

VectorQ UID:96
2026-10-04 13:50
9 4 0

Web 应用的漏洞清单年年在变,但排在前面的几位常年不换名字。SQL 注入、跨站脚本(XSS)、跨站请求伪造(CSRF)这三类问题,本质上都不是”框架不够新”造成的,而是同一个根因的不同表现:程序把不可信的数据,当成了指令或上下文的一部分来执行。搞清楚这条主线,防御方案就不再是零散的口诀。

一、共同的根因:数据与代码的边界

任何 Web 程序都在处理两类东西:程序自己写的代码/模板/查询,和用户带来的数据。只要两者在一个字符串里被一起拼出来再执行,边界就模糊了:

  • 拼进 SQL 语句 → SQL 注入;
  • 拼进 HTML/JS 上下文 → XSS;
  • 拼进系统命令 → 命令注入(同一家族)。

所以防御的第一原则只有一条:永远不要用字符串拼接把用户数据拼进”要被执行的东西”里。这条原则落实到不同上下文,才衍生出参数化查询、输出编码、Token 校验这些具体手段。

二、SQL 注入:把数据变成 SQL 语法

成因

假设后端用字符串拼接生成查询,把用户输入的用户名直接嵌进 SQL 文本。数据库看到的不再是一个”值”,而是一个包含用户文本的完整语句——用户输入里如果含 SQL 语法,就改变了语句本身的结构。后果轻则是绕过登录条件、多查出数据,重则是数据被篡改或拖走。

注入点不一定在表单里。URL 参数、请求头、排序字段名、分页参数、JSON 字段,凡是最终参与 SQL 拼接的地方,都属于同类风险面。

防御原理

核心手段是参数化查询(预编译):把 SQL 的结构和数据分成两部分,先让数据库编译语句结构,再把用户输入作为”值”绑定进去。值在绑定阶段就失去了语法意义,无论内容里写了什么,都只被当成字面数据。

# 错误:用户输入拼进了语句结构
sql = f"SELECT * FROM users WHERE name = '{name}'"

# 正确:结构固定,数据走绑定参数
cursor.execute("SELECT * FROM users WHERE name = ?", (name,))

配套的纵深防御还有:用 ORM/查询构造器时避免 raw 拼接入口;给数据库连接配置最小权限账号(应用账号不该有删库、读系统表的权限);错误信息不要把 SQL 原文抛给前端。需要动态拼接表名、排序字段这类”没法参数化”的位置,只能走严格白名单,不能直接用用户值。

三、XSS:把数据变成脚本

成因

XSS 的本质是:用户可控的内容被原样输出到了浏览器会执行的上下文里。页面本该把它当文本渲染,结果浏览器把它当成了 HTML 标签或 JavaScript。危害是脚本以受害者站点的身份运行,能读到当前页面的会话信息、冒充用户发起操作。

XSS 常见三种形态:存储型(恶意内容存进数据库,每个访问者都中招)、反射型(恶意内容随请求回显到响应里,通常靠诱导链接触发)、DOM 型(数据没经过服务端,直接在前端脚本里被写入危险 API)。三者的共同点都是”输出时没有按上下文转义”。

防御原理

输出编码(context-aware escaping)是根本解:在把数据插入 HTML 的那一刻,按它所处的上下文做转义。HTML 文本、HTML 属性、JS 字符串、URL 查询参数,各自的转义规则不同,所以模板引擎默认的自动转义很重要——别轻易关掉。

其他关键手段:

  • CSP(内容安全策略):用响应头限制脚本来源,把”能执行什么”从数据内容里剥离出来。即便某处漏了转义,CSP 也能显著抬高利用门槛。
  • 避免危险 API:前端不要把不可信数据传给 innerHTML、eval、字符串形式的 setTimeout 等会解析执行的位置,改用 textContent 或安全的 DOM 操作。
  • HttpOnly Cookie:让脚本读不到会话 Cookie,降低 XSS 直接偷会话的收益。
  • 富文本场景必须做白名单过滤(允许哪些标签、哪些属性),而不是黑名单删标签。

四、CSRF:借用户的”登录态”发请求

成因

CSRF 的前提是浏览器会自动带上 Cookie。用户登录了站点 A,此时访问了恶意页面 B,B 触发的对站点 A 的请求(比如一个表单提交)会被浏览器自动附上 A 的会话 Cookie——A 的服务器分不清这个请求是用户本意还是被第三方页面驱动的。攻击者不需要知道密码,只需要利用”已经登录”这个状态。

CSRF 改的通常是状态:转账、改密码、改邮箱、发内容。所以 GET 请求不该产生副作用,这也是被反复强调的设计约束。

防御原理

  • CSRF Token:服务端给每个会话生成随机 Token,写入表单或请求头;服务器校验请求里带的 Token 是否与会话匹配。恶意页面拿不到这个 Token,伪造的请求就过不了校验。
  • SameSite Cookie:把 Cookie 标记为 SameSite=Lax 或 Strict,浏览器就不再在跨站请求里自动携带它。这一层防护成本极低,覆盖面广。
  • 二次确认敏感操作:改密码、绑定邮箱等高危操作要求重新验证身份(密码、2FA),把”一次点击”变成多步。
  • 关键接口同时校验 Origin / Referer 与预期站点是否一致,作为辅助手段。

Token 防护和 SameSite 最好同时上——前者是应用层校验,后者是浏览器层隔离,单靠任何一层都可能被特定场景绕开。

五、防御清单

漏洞 一句话成因 核心防御 纵深补充
SQL 注入 数据拼进了 SQL 结构 参数化查询 最小权限账号、白名单列名、错误信息脱敏
XSS 数据未转义进了执行上下文 按上下文输出编码 CSP、HttpOnly、富文本白名单
CSRF 浏览器自动带上了会话凭据 CSRF Token + SameSite 敏感操作二次验证、校验 Origin

放到更大的视野里,OWASP Top 10 这类清单的价值不在”背下十项”,而在它提供了一个分类框架:注入类、失效的访问控制、不安全的配置、依赖组件风险……条目会随时代更替,但”数据与指令分离””默认最小权限””纵深防御”这几条原则不会。写代码时把这三类漏洞想明白,比照着清单逐项打勾更有用。

日常排查时可以顺着数据流走一遍:每一个来自外部的值,最终流进了 SQL、HTML、命令行还是序列化器;在那个终点上,它被当成”数据”还是被当成”结构”。只要这条链路上有一处把它当成了结构,漏洞就在那里。这个视角比记漏洞名字更耐用,也能覆盖清单之外的同类问题,比如模板注入、路径拼接、反序列化滥用——它们的成因与上面三类是同一个故事的不同章节。

安全防御的正确姿势始终是:在正确的位置用正确的转义/校验,把风险消灭在数据进入”可执行上下文”之前,而不是靠事后的检测去兜底。

111浏览量
9赞
4评论
3参与用户
8 分钟阅读时间

评论(4)

  • Soraya · 2026-10-04 16:50

    看完第一遍没懂,第二遍看明白了,确实需要点基础。

    回复
  • 硅屿 · 2026-10-04 23:50

    踩过同样的坑,当时没人告诉我这些。

    回复
  • 硅屿 · 2026-10-05 06:50

    这个视角挺新的,以前没这么想过。

    回复
  • 许清欢 · 2026-10-05 13:50

    这类内容对新手很友好。

    回复