概念定义
第二因子本身成了杠杆:推送足够多的提示,总有一次被批准。用户为了让手机停止震动而点同意,或相信“客服”所说的例行验证。认证模型没有坏,坏的是那个人的决策点。
工作机制
持有有效凭据的攻击者反复发起认证触发通知。防御:数字匹配(要求用户输入登录屏幕显示的码)、审批限速与锁定、展示请求来源的上下文提示、管理员迁移到硬件安全密钥等防钓鱼因子。
使用场景
一切基于推送的 MFA 面,特权账号优先。技术控制要配合客服政策:身份验证流程绝不包含“批准一个意外提示”。
常见误区
“推送 MFA 比 SMS 弱”是误判——因子没问题,审批交互才是攻击面,数字匹配即可关闭。对每次提示都告警只会训练用户更快划掉通知;重试无限次等于给脚本轰炸开绿灯。数字匹配在攻击者同时控制显示码的屏幕时会退化;限速必须配账号级锁定,又不能变成对已知用户的拒绝服务武器。
代码示例
export function escapeRegExp(input) {
if (typeof input !== "string") throw new TypeError("input must be a string");
return input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
export function literalRegExp(input, flags = "") {
if (typeof flags !== "string") throw new TypeError("flags must be a string");
if (/[gy]/.test(flags)) {
throw new RangeError("stateful flags g and y are not allowed");
}
return new RegExp(escapeRegExp(input), flags);
}
package main
import (
"crypto/sha256"
"crypto/subtle"
"fmt"
)
func ConstantTimeEqual(a, b string) bool {
left := sha256.Sum256([]byte(a))
right := sha256.Sum256([]byte(b))
return subtle.ConstantTimeCompare(left[:], right[:]) == 1
}
func main() {
secret := "s3cr3t-token"
fmt.Println(ConstantTimeEqual(secret, "s3cr3t-token"))
fmt.Println(ConstantTimeEqual(secret, "s3cr3t-tokeX"))
fmt.Println(ConstantTimeEqual(secret, "short"))
}