news 2026/9/28 21:20:40

杂记02 逻辑漏洞

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
杂记02 逻辑漏洞

⚠️免责声明:本文仅用于授权范围内的安全测试、CTF 靶场练习与防御建设。未经授权对他人系统进行测试属于违法行为。文中所有技术细节的目的,都是帮助开发者理解攻击原理、进而写出更安全的代码。

一句话概括:业务逻辑漏洞不是"代码写错了",而是"流程设计漏了"——代码本身能正常跑,只是没考虑到有人会不按设计好的流程走。


一、先搞清楚:业务逻辑漏洞到底"漏"在哪

1.1 与 SQL 注入 / XSS 的本质区别

维度代码层漏洞(SQL 注入 / XSS)业务逻辑漏洞
成因代码语法 / 函数使用错误业务流程设计缺陷
表现有明确的危险函数、明确 payload一步步走正常流程,只是"跳了一步"
检测工具扫描器能发现(sqlmap、Xray)扫描器基本发现不了,必须人工理解业务
修复改一个函数、加一层过滤重新设计流程,可能要改产品逻辑
典型例子' or 1=1#打进查询收自己的验证码,去改别人的密码

关键认知:业务逻辑漏洞的发现方式不是"扫描",而是"把正常流程拆开,问自己:如果这一步我不做 / 多做 / 乱做,系统会拦我吗?"

1.2 通用测试思路

正常流程:A → B → C → D → 完成 攻击思路:逐个环节下手 ① 跳过:不做 B,直接请求 C (例如不输验证码直接提交) ② 篡改:把 B 请求里的参数改成别人的(越权) ③ 重放:把 B 请求原样再发一次(重复使用) ④ 枚举:把 B 请求里的值一个个试(爆破) ⑤ 逆序:先做 D 再做 B (顺序依赖缺陷) ⑥ 并发:同时发两次 B (竞态,薅羊毛的经典手法)

📌记住六个词:跳过、篡改、重放、枚举、逆序、并发。绝大多数业务逻辑漏洞都落在这六类里。

1.3 它在 OWASP Top 10 里算哪一类?

很多文章会说"业务逻辑漏洞属于失效的访问控制",这个说法不准确。查过 OWASP Top 10:2021 官方文档后,准确归类如下:

具体表现OWASP Top 10:2021 分类
验证码 / 流程被绕过、并发薅羊毛、频率不设限A04 不安全设计(Insecure Design)
改 Cookie / 改 userid 切换用户A01 失效的访问控制
弱口令、爆破、用户名枚举、明文存密码A07 身份识别和认证失败

A04 页面明确映射了下面这几个 CWE,建议记住编号:

  • CWE-840Business Logic Errors(业务逻辑错误)

  • CWE-841Improper Enforcement of Behavioral Workflow(行为流程执行不当)

  • CWE-799Improper Control of Interaction Frequency(交互频率控制不当)← 短信轰炸就是这条

  • CWE-602Client-Side Enforcement of Server-Side Security(用前端实现服务端安全)← "前端做了校验"就是这条

💡顺带一提:OWASP Top 10 对业务逻辑漏洞的覆盖本来就有限(它面向"通用风险类别")。系统讲逻辑漏洞的是 OWASP 的WSTG(Web Security Testing Guide)里的 Business Logic Testing 章节,以及OWASP ASVS。别指望靠背 Top 10 找逻辑漏洞。


二、短信验证码漏洞

2.1 正常业务全链路

用户输入手机号 → 前端校验格式 + 启动 60s 倒计时 → 调用后端发送接口 → 后端生成验证码(4 / 6 位) → 绑定手机号存入 Redis / MySQL(设置 TTL) → 调用第三方短信服务商 → 下发到用户手机 → 用户输入验证码,提交校验接口 → 后端三重校验(验证码匹配 + 未过期 + 未使用) → 标记已使用 → 执行业务(登录 / 改密 / 绑卡)

🎯核心原则:验证码的生成、存储、校验、限流,全部必须在后端完成。前端做的一切(格式校验、倒计时、隐藏输入框)都只是体验优化,不能作为安全防线。

2.2 技术栈与存储设计

层级常见技术说明
后端Java / Python / PHP / Go / Node.js实现验证码业务逻辑
存储Redis(首选)/ MySQLRedis 支持 TTL 自动过期、读写快,适合临时数据
短信通道主流云厂商短信服务 / 专业短信服务商第三方 API 对接运营商
前端HTML + JS手机号格式校验、60s 倒计时(仅 UI,可绕过)
测试工具Burp Suite(Repeater / Intruder)抓包、重放、爆破
协议HTTP / HTTPS(JSON)前后端接口通信

Redis 存储的合理设计(示意):

key: sms:code:138xxxx1234 ← 手机号是 key 的一部分(这就是"绑定") value: 8f4a1c ← 不要存明文,存哈希更稳妥 ttl: 300s(5 分钟)

⚠️ 注意key里带手机号,意味着"这个验证码属于这个手机号"——这正是不绑定漏洞的防线所在。如果 key 写成sms:code:<随机id>,而校验时只比对 value,就会出现"A 的码能验 B 的手机"。

2.3 六大常见漏洞类型速览

漏洞成因危害
验证码回显响应包明文返回验证码,调试代码遗留抓包直接读取,无需收短信
短信轰炸发送接口无后端限流,只靠前端倒计时可无限调用,骚扰任意手机号
验证码与手机号未绑定只存验证码,未关联手机号 / 会话A 手机验证码可验证 B 手机,接管他人账号
后端不校验验证码校验逻辑写在前端,后端省略校验任意输入均可通过
验证码暴力破解无失败次数限制,纯数字可枚举批量爆破,登录任意账号
验证码可重复使用校验通过后未标记作废同一验证码反复使用

2.4 逐条拆解:怎么测、怎么防

(1)验证码回显

现象:发送验证码的响应包里直接带着验证码。

// ❌ 危险的响应 {"code": 0, "msg": "发送成功", "data": {"verifyCode": "886655"}}

为什么会出现:开发阶段为了方便调试,把验证码打进日志和响应,上线时忘删;或者短信通道没配好时用来"兜底"。

怎么测:发送验证码时抓包,看响应体、响应头、以及Set-Cookie里有没有那串数字。

怎么防:响应包只返回{"code": 0, "msg": "发送成功"},验证码只进服务端存储,不进任何返回给客户端的数据。

(2)短信轰炸

现象:前端有 60 秒倒计时,但后端完全没限流。

测试要点:

  • 把发送请求用 Repeater 连续重放 10 次 → 若手机收到 10 条 → 确认无后端限流。

  • 后端可能只按手机号限流或只按 IP 限流,这就是绕过点:

    • 只按手机号限流 → 用手机号的等价写法(+86前缀、空格分隔等)试探后端是否做了规范化;

    • 只按 IP 限流 → 观察是否需要X-Forwarded-For,以及后端是否信任它。

  • 再注意多接口:注册、登录、找回密码可能是三个不同的发送接口,限流往往只做了其中一个。

危害:短信是要花钱的(可能被刷爆短信费),同时会对任意手机号造成骚扰。

怎么防:手机号 + IP + 设备三重限流(如 60s / 1 次、单日上限);图形验证码 / 滑块前置;对短信服务商侧也做余额告警。

(3)验证码与手机号未绑定 —— 危害最大的一条

攻击模型(这是最有价值的理解):

攻击者用自己的手机号 138xxxx0001 请求验证码 → 收到真实验证码 886655 → 提交校验时,把请求里的 phone 参数改成受害者的 139xxxx0002 → 若后端只校验"886655 对不对",不校验"这个码是不是 139xxxx0002 的" → 攻击者通过校验,登录 / 重置了受害者的账号

根本原因:校验接口信任了请求里的手机号,而不是使用服务端会话中记录的手机号。

怎么测:抓取校验请求,改phone/mobile/username参数为目标手机号,看是否通过。

怎么防:校验时必须用服务端记录的手机号(与当前会话绑定),请求里的手机号只作展示用途;验证码 key 必须包含手机号。

(4)后端不校验验证码

现象:校验逻辑写在前端 JS 里,或者后端接口干脆不比对。

测试要点:

  • 随便填个 6 位数(如000000)提交;

  • 或者直接把验证码参数删掉,看接口是否仍然返回成功;

  • 或者把code参数改成null/ 空字符串。

怎么防:校验逻辑只写在后端;参数缺失时按"校验失败"处理,不能因为参数没传就跳过校验——这是最典型的"参数污染"式疏漏。

(5)验证码暴力破解

先算一笔账:

位数组合数无限制时爆破耗时(按 10 次/秒)
4 位纯数字10,000≈ 17 分钟
6 位纯数字1,000,000≈ 28 小时

看起来 6 位"很安全",但 Burp Intruder 多线程跑起来、或用脚本直接压接口,几小时就能覆盖。所以位数只是拖延时间,真正的防线是"失败次数限制 + 短有效期"。

怎么测:连续提交错误验证码 20 次,观察是否出现锁定、延迟、验证码失效或需要重新获取。

怎么防:同一手机号 / 会话对同一验证码最多错 5 次即作废并强制重新获取;有效期压到 5 分钟以内。

(6)验证码可重复使用

现象:验证码校验通过后没有被删除,同一个码可以反复提交成功。

怎么测:校验成功后,把同一个请求原样重放一次(Burp Repeater 直接 Send),看是否仍然返回成功。

怎么防:校验成功的那一刻立即删除该验证码(先在 Redis 里DEL,再执行业务),保证一次性消费。

2.5 渗透测试 Checklist

获取验证码阶段:

  • 抓包看响应包(含响应头、Cookie),是否有验证码回显
  • 重复发送请求,测试是否有后端限流(短信轰炸)
  • 前端倒计时能否绕过(抓包直接重放)
  • 注册 / 登录 / 找回密码三个接口是否都做了限流

提交验证码阶段:

  • 随便输入数字(如000000),测试后端是否真的校验
  • 删除验证码参数,测试是否会"跳过校验"
  • 多次提交错误验证码,测试是否有失败次数限制
  • 用 A 手机号的验证码去验证 B 手机号,测试绑定是否生效
  • 校验成功后重放同一请求,测试是否一次性作废
  • 修改请求中的手机号参数,测试越权

2.6 防御方案汇总

  1. 防回显:响应包严禁返回验证码明文(含响应头、日志)

  2. 服务端校验:校验逻辑必须全部在后端,且参数缺失按失败处理

  3. 强绑定:验证码与手机号、会话三者绑定,校验用服务端的手机号

  4. 一次性:校验通过立即删除,保证只用一次

  5. 防爆破:失败 5 次锁定 + 有效期 ≤ 5 分钟 + 尽量用 6 位

  6. 防轰炸:手机号 + IP 双重限流(60s / 1 次、单日上限)+ 图形验证码 / 滑块


三、登录认证漏洞

3.1 正常登录流程

用户输入账号密码 → 前端做非空 / 格式校验 → 提交表单 → 后端拼接 SQL 查询数据库(❌ 危险)/ 参数化查询(✅ 正确) → 匹配成功 → 创建 Session / Cookie → 进入系统 → 匹配失败 → 返回错误提示

身份认证的核心同样是:后端校验,不能只依赖前端。

3.2 通关思路一:暴力破解(弱口令爆破)

  • 原理:系统没有登录失败次数限制,用字典批量遍历"账号 + 密码"组合。

  • 工具:Burp Suite → Proxy 抓包 → 右键Send to Intruder→ 加载字典(Sniper / Cluster bomb 模式)。

  • 判断成功的三个维度(不要只看页面文字):

    1. 响应长度——失败提示固定,成功必然长度不同;

    2. HTTP 状态码——302 跳转通常意味着登录成功;

    3. 响应内容——出现用户名、"欢迎回来"等关键词。

  • 前提:无失败次数限制且存在弱口令(这两条缺一不可)。

💡实战经验:先跑一遍admin/admin、admin/123456、admin/admin888这类高频组合,往往不用上字典就进去了。同时留意账号大小写、前后空格,以及 JSON 与 form 两种提交格式是否被同一套逻辑处理。

3.3 通关思路二:SQL 注入万能密码

原理:后端直接拼接 SQL、没有预编译,攻击者闭合单引号后篡改查询逻辑。

Payload 示例:

admin' or '1'='1 ' or 1=1 limit 1#

拼接后的效果(以SELECT * FROM users WHERE username='$u' AND password='$p' LIMIT 1为例):

输入 u = ' or 1=1 limit 1# 拼接结果: SELECT * FROM users WHERE username='' or 1=1 limit 1#' AND password='任意值' LIMIT 1 ↑ 注释符,后面全被丢掉

1=1恒为真 →or让整个条件永真 → 返回第一条用户数据 → 校验通过。

容易忽略的细节一:payload 里的limit 1是干什么的?

因为or 1=1会让查询返回全部用户行。而后端代码通常只fetch一行、或对结果集取[0],一旦返回多行就可能抛异常或走偏逻辑,反而"打不进去"。加上limit 1就能稳定只取第一行,兼容性最好。

⚠️ 注意区分两个LIMIT 1:模板里自带的那个是开发者写的(被 payload 的#注释掉了),payload 里补的这个是攻击者加的。所以万能密码的"标准姿势"是' or 1=1 limit 1#,而不是裸的' or 1=1。

容易忽略的细节二:admin' or '1'='1为什么能过?

这个 payload没有注释符,它的原理完全不同——靠的是or的短路求值:

输入 u = admin' or '1'='1 拼接结果: SELECT * FROM users WHERE username='admin' or '1'='1' AND password='任意值' LIMIT 1

因为AND优先级高于OR,实际等价于:

WHERE (username='admin') OR (('1'='1') AND (password='任意值'))

只要username='admin'成立,左边为真,整个 WHERE 就为真,密码压根不参与判断。

🔍推论:这个 payload依赖admin账号真实存在——如果用户名不存在,左边为假,就退化成'1'='1' AND password='任意值',照样需要正确密码。所以它的实际效果是"登录成 admin",而不是"随便登成一个用户"。这也解释了为什么它通常写作admin' or '1'='1、而不是裸的' or '1'='1。

关于注释符:MySQL 里#和--(必须跟一个空格)都能注释,裸写--会语法报错。所以等价写法是' or 1=1 limit 1#或' or 1=1 limit 1--+(URL 里+会被解码成空格)。

本质:这是 SQL 注入漏洞。之所以放在"逻辑漏洞"里讲,是因为它攻击的目标是"认证流程"——不是猜出密码,而是让流程直接放行。

3.4 两种思路对比

对比暴力破解万能密码
原理猜真实账号密码利用 SQL 注入篡改查询逻辑
前提无失败限制 + 弱口令存在 SQL 注入漏洞(未预编译)
速度慢,依赖字典质量快,一步到位
修复方向加失败次数限制 + 强密码策略改参数化查询(预编译)

3.5 登录模块常见漏洞汇总

  1. 暴力破解 / 弱口令:无失败次数限制,账号使用简单密码

  2. 用户名枚举:报错信息差异化("用户名不存在" vs "密码错误"),可先遍历出合法用户名

  3. SQL 注入(万能密码):直接拼接 SQL,未参数化

  4. 前端校验绕过:校验逻辑写在前端 JS,抓包修改即可绕过

  5. Cookie / Session 越权:修改 Cookie 中的userid直接切换用户

  6. 记住我漏洞:Cookie 加密缺陷,可伪造身份

💡关于用户名枚举:不要只盯着登录接口的报错文案,还要看HTTP 状态码、响应长度、响应时间(用户存在时要查库 + 验哈希,会稍慢),以及注册接口("该用户名已被占用"是最直接的枚举入口)和找回密码接口。后两者往往比登录接口更容易枚举。

3.6 补充:密码找回流程(逻辑漏洞的重灾区)

原稿没有这一节,但 OWASP Top 10 的 A04 页面第一个攻击场景就是它,值得单独列出:

  • 密保问题:"你母亲的姓氏是?"这类问题被NIST SP 800-63b和 OWASP ASVS 明令禁止——答案往往能从社交媒体查到,而且不止一个人知道。现代系统应当直接删除这个设计。

  • 改密接口不校验旧密码:已登录状态下调用改密接口,不要求提供原密码 → 结合 XSS 或 CSRF 可直接改密。

  • 重置 token 可预测 / 可枚举:token 是自增 id、时间戳、简单位置编码等。

  • 验证码复用:重置密码的短信验证码没有一次性作废,可反复使用。

  • 步骤跳跃:验证身份 → 设置新密码 是两步流程,若直接请求第二步接口就能改密。

测试思路:把"找回密码"多步流程的每一步都单独抓出来,尝试直接请求最后一步。

3.7 测试标准步骤

① 测试报错信息 → 判断是否可枚举用户名 ② 尝试万能密码 → 测试 SQL 注入 ③ 抓包重放 → 测试是否可暴力破解 ④ 检查 Cookie/Session → 测试越权登录 ⑤ 走一遍找回密码 → 测试流程能否跳跃、token 能否枚举

3.8 防御方案

  1. 防爆破:失败次数限制(5 次锁定)、人机验证、IP 限流

  2. 防注入:强制参数化查询(预编译),禁止直接拼接 SQL

  3. 统一报错:返回统一的"用户名或密码错误",防止用户名枚举

  4. 密码安全:加盐哈希存储(bcrypt/argon2),禁止明文,禁止弱口令

  5. 不信任前端:所有身份校验在后端执行

  6. 会话安全:服务端存储会话状态,客户端只持有不可预测的随机 token;Cookie 设置HttpOnly+Secure+SameSite

  7. 找回密码:敏感操作要求二次验证(旧密码或已验证的会话),token 使用密码学随机数且一次性有效


四、核心原则速记

业务逻辑漏洞通用测试思路:把正常流程的每一步拆开,逐个尝试"跳过、篡改、重放、枚举、逆序、并发"。

❌ 错误做法✅ 正确做法
前端校验,后端不校验所有安全判断必须在服务端
参数缺失就跳过校验参数缺失按"校验失败"处理
前端倒计时,后端不限流后端做手机号 + IP 双重限流
验证码不绑定手机号验证码与手机号、会话强绑定
返回包明文返回验证码响应包绝不泄露敏感信息
不限制尝试次数限制失败次数,超限锁定
验证码永久有效短有效期 + 一次性作废
报错信息差异化统一报错,防止枚举
密码明文存储加盐哈希存储(bcrypt / argon2)
用自增 id / 时间戳当重置令牌用密码学随机数当一次性令牌

五、总结

5.1 一句话理解逻辑漏洞

代码层漏洞是"墙有洞",业务逻辑漏洞是"门没锁"——墙和门看起来都很完整,只是没人想到有人会从没锁的门走进来。

5.2 记住三件事

  1. 扫描器发现不了逻辑漏洞,靠的是人工理解业务 + 拆解流程。

  2. "前端做了"等于"没做"(CWE-602)。一切安全判断都要在后端重做一遍。

  3. 验证码的四个必须:必须服务端生成、必须绑定手机号与会话、必须短时有效、必须一次性作废。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 21:20:13

大模型三层架构实战:输入输出处理才是AI应用落地的胜负手

1. 大模型三层架构到底在拆什么第一次听到“大模型三层架构”这个说法&#xff0c;很多人会下意识往MVC、物联网三层架构那边联想&#xff0c;觉得是不是又搞了个新名词来包装老概念。其实不是。我做了几年AI应用落地&#xff0c;从早期调API拼Demo&#xff0c;到后来自己搭推理…

作者头像 李华
网站建设 2026/9/28 21:19:16

橡胶软接头执行标准怎么看:先确认标准范围和产品类型

看橡胶软接头执行标准&#xff0c;正确顺序是先确认标准覆盖的范围&#xff0c;再把手里的产品类型对应进去。目前常被引用的参考口径是 GB/T 26121-2010《橡胶软接头》&#xff0c;它有官方全文&#xff0c;规定了术语、分类、要求、试验、检验、标志、包装与贮运。需要说明&a…

作者头像 李华
网站建设 2026/9/28 21:18:46

UVA-1149 装箱 题解答案代码 算法竞赛入门经典第二版

GitHub - jzplp/aoapc-UVA-Answer: 算法竞赛入门经典 例题和习题答案 刘汝佳 第二版 方法比较简单&#xff1a; 首先选择当前最大的元素&#xff0c;然后再选一个最大为l-这个元素 的元素&#xff0c;且是符合条件中最大的。 存储使用map&#xff0c;好处如下&#xff1a; 1…

作者头像 李华
网站建设 2026/9/28 21:17:57

MangoDisk CLI使用教程:自动化磁盘清理,JSON输出轻松接入脚本

MangoDisk CLI使用教程&#xff1a;自动化磁盘清理&#xff0c;JSON输出轻松接入脚本 【免费下载链接】MangoDisk Safety-first disk cleaner and space analyzer for macOS and Windows, with duplicate cleanup, app uninstall, startup management, system optimization, an…

作者头像 李华