跳转到内容

Web 安全

Web 漏洞的共同主题是**"数据与代码的边界":几乎所有的注入类漏洞都源于把不可信数据当成了代码**。本章讲 SQL 注入、XSS、CSRF、同源策略与 CORS、点击劫持——每一类都回到这条边界。

# 危险:字符串拼接——数据成了 SQL 代码
cursor.execute(f"SELECT * FROM users WHERE name = '{name}'")
# name = "x' OR '1'='1" → 全表返回;"x'; DROP TABLE users; --" → 任意执行
# 正确:参数化——数据永远是数据
cursor.execute("SELECT * FROM users WHERE name = %s", (name,))

注入的根治是参数化,不是过滤:

  • 参数化查询(prepared statement):SQL 结构与数据分离——数据永远是数据,这是唯一根治;
  • 过滤/转义是补丁:黑名单遗漏、编码绕过——作为纵深可以,作为防线不够;
  • 最小权限的数据库账号:应用账号不给 DROP/FILE 权限——注入的爆炸半径控制(→ 认证与授权)。

同样的边界问题在每种"解释器"前重现:命令注入(shell 拼接)、模板注入(SSTI)、LDAP/XPath 注入——根治都是结构化参数或 API,不是字符串拼接。

跨站脚本(XSS):不可信数据进入浏览器执行的代码(HTML/JS)——"把数据当成了前端代码"。三类:

类型注入点特点
存储型服务器存下的恶意内容(评论、简介)每个访问者都中,危害最大
反射型URL 参数回显进页面钓鱼链接诱导
DOM 型前端 JS 处理不可信数据(innerHTML)不经过服务器

XSS 的后果:以受害者身份执行任意操作(读 Cookie/令牌、发请求、改页面)——比 SQL 注入更贴近"完全接管"(受害者是活着的用户)。

  • 输出编码:进入 HTML/属性/JS/URL 各有各的编码——上下文敏感(HTML 实体 ≠ JS 转义 ≠ URL 编码);
  • 模板引擎默认转义 + 别用 innerHTML/dangerouslySetInnerHTML 塞不可信内容;
  • HttpOnly Cookie:JS 读不到会话 Cookie——令牌窃取的主路径被切断;
  • CSP(内容安全策略):CSP 是 XSS 的纵深防御——白名单脚本来源、禁内联脚本:编码被绕过时,注入的脚本也执行不了。

同源策略(SOP)是浏览器安全的基石:不同源(协议+域+端口)的脚本不能读彼此的数据——evil.com 的 JS 不能读 bank.com 的响应。它隔离的是读取,不是发送(跨站请求可以发出去,见 CSRF)。

CORS 是放宽同源策略的受控机制——不是"安全开关",是"显式允许":

  • 服务器返回 Access-Control-Allow-Origin 声明允许的源;
  • 预检(preflight):非简单请求先发 OPTIONS 问许可;
  • 凭据模式:带 Cookie 的跨站请求要求显式 Allow-Credentials + 非通配源。

常见错误:Access-Control-Allow-Origin: * 配合凭据(浏览器会拒绝,但代理/自写客户端会漏);用 CORS"隐藏" API(它不保护——非浏览器客户端不受 SOP 约束)。服务端授权校验才是防线,CORS 只是浏览器的规则。

跨站请求伪造(CSRF):利用"浏览器自动带 Cookie"——evil.com 的页面发一个 <img src="https://bank.com/transfer?to=attacker&amount=100">,浏览器带着你在 bank.com 的 Cookie 发出,服务器以为是本人操作。

同源策略不防 CSRF:SOP 不让 evil.com 读响应,但请求已经发出去了(副作用已发生)。

防御:

  • CSRF token:表单带随机令牌,服务端校验——跨站拿不到令牌;
  • SameSite Cookie(Lax/Strict):跨站请求不带 Cookie——现代浏览器默认 Lax,已挡住大半;
  • 校验 Origin/Referer 头;
  • GET 无副作用:改变状态的请求用 POST + token(GET 被 img/link 触发)。

点击劫持(clickjacking):透明 iframe 叠在诱饵上——受害者点击"领奖",实际点的是"授权/删除"。防御:X-Frame-Options: DENY / CSP frame-ancestors(禁止被嵌入 iframe)。

漏洞边界被破坏的地方根治
SQL 注入数据 → SQL 代码参数化
XSS数据 → 浏览器代码输出编码 + CSP
命令注入数据 → shell 代码结构化 exec
模板注入数据 → 模板代码数据与模板分离

漏洞几乎都源于把不可信数据当成了代码——审查任何新接口时问一句:"这条数据流到解释器/执行器那里了吗?边界在哪里?"

  • 共同主题:数据与代码的边界;注入的根治是参数化,不是过滤
  • XSS 三类(存储/反射/DOM);防御 = 输出编码(上下文敏感)+ HttpOnly + CSP 纵深
  • 同源策略是浏览器安全的基石;CORS 是受控放宽,不是防线
  • CSRF 利用浏览器自动带 Cookie;防御 = SameSite + token + GET 无副作用
  • 点击劫持靠 frame-ancestors 防御