2. 身份认证基础
在讨论疲劳攻击与验证码滥用之前,必须先回到身份认证的基本盘:系统究竟凭什么相信“你是你”。单因素认证只验证一类证据,一旦密码泄露,攻击者即可完成登录。MFA(Multi-Factor Authentication,多因素认证)要求用户提交两类或以上不同性质的凭证,把“知道什么”“拥有什么”“是什么”组合起来,从而显著抬高账号接管成本。理解 MFA 的因子分类、推送认证的交互链路以及它的安全边界,是后续识别 MFA 疲劳攻击(MFA Fatigue / Push Bombing)的前提。
一、核心原理:三类因子与“不同性质”原则
MFA 的强度不来自因子数量,而来自因子之间的独立性。业界通常把认证因子分为三类:
- 知识因子(Something you know):密码、PIN、口令短语、安全问题答案。易被钓鱼、撞库、肩窥获取。
- 持有因子(Something you have):手机、硬件令牌、智能卡、FIDO2 安全密钥、TOTP 应用。核心是“私钥或种子不出设备”。
- 生物因子(Something you are):指纹、人脸、声纹、虹膜。便捷但不可撤销,一旦泄露无法更换。
如果两个因子同属一类,例如“密码 + 安全问题”,那只是两道知识题,不构成真正的 MFA。同理,“密码 + 短信验证码”虽然分属知识与持有,但短信可被 SIM 交换、拦截或社会工程转发,属于弱持有因子。真正抗钓鱼的是绑定源站身份的 FIDO2/WebAuthn,因为验证器会把凭据绑定到具体域名,钓鱼站点无法复用。
二、常见因子类型与推送认证流程
推送认证(Push-based Authentication)是当前企业最主流的 MFA 形态之一,典型代表是 Microsoft Authenticator、Duo Push、Okta Verify。其流程可概括为“绑定—挑战—批准—断言”四步:
- 注册绑定:用户在身份提供商(IdP)处扫码或输入激活码,设备生成密钥对,公钥上传 IdP,私钥留在安全 enclave。
- 发起挑战:用户提交用户名密码后,IdP 生成一次性挑战(challenge),通过推送通道下发到已注册设备。
- 用户批准:用户在手机上看到“某地某应用正在登录”,点击“批准”或输入匹配数字,设备用私钥对挑战签名。
- 验证断言:IdP 用公钥验签,通过后签发会话令牌(如 SAML Assertion 或 OIDC ID )。
下面是一段典型的 OIDC 推送认证交互日志片段,可用于后续检测规则设计:
event: mfa.challenge.created
user: alice@corp.example
method: push
device_id: 8f3c-...-a91
ip: 203.0.113.45
geo: Singapore
result: pending
event: mfa.challenge.approved
user: alice@corp.example
device_id: 8f3c-...-a91
latency_ms: 4200
result: success
注意日志中的 latency_ms、geo、device_id 三个字段:正常用户从收到推送到点击批准通常需要数秒到数十秒,且地理位置与登录 IP 大体一致。若出现“登录 IP 在境外、批准延迟仅 800ms、设备指纹陌生”的组合,就高度可疑——这正是疲劳攻击的典型特征。
三、案例场景:一次典型的推送疲劳攻击
攻击者已通过钓鱼或撞库拿到 Alice 的密码,但缺少第二因子。于是攻击者脚本化地反复触发推送认证,每分钟向 Alice 手机发送数十条“批准登录”请求。凌晨时段,Alice 被持续弹窗骚扰,最终误点“批准”,攻击者随即获得会话。整个过程没有漏洞利用,只有对人的消耗。
该场景暴露了推送认证的三个结构性弱点:
- 批准动作过于廉价:一次点击即可放行,缺少上下文确认(如匹配数字、显示来源 IP)。
- 无频率与节流约束:IdP 默认允许短时间内重复推送,给攻击者制造“轰炸”空间。
- 用户缺乏拒绝与上报路径:多数 App 只有“批准/拒绝”,没有“这不是我,上报”的一键反馈。
四、误区复盘与安全边界
实践中常见以下误区,需要在课程后续的检测设计中反复对照:
| 误区 | 问题本质 | 正确做法 |
|---|---|---|
| “上了 MFA 就安全了” | 忽略因子强度与实现差异 | 区分弱持有因子与抗钓鱼因子,优先 FIDO2 |
| “短信验证码也是 MFA” | 短信可被 SIM 交换与拦截 | 将短信视为降级方案,关键操作强制强因子 |
| “推送批准越快越好” | 把用户体验等同于安全 | 引入数字匹配与来源上下文,允许合理延迟 |
| “只记录成功登录” | 丢失失败与待批准信号 | 采集 challenge.created / denied / timeout 全链路日志 |