设想你是一家银行保险库的VIP客户,每次取款都要填一张取款单。这张单据上有你的签名、日期和一个临时取款编号。小偷如果从垃圾桶里捡到你用过的取款单,趁柜员不注意再递进去一次,就能冒领你的钱——这就是“回放攻击”:攻击者截获一次合法的API请求,在服务器毫不知情的情况下把同一份请求“重放”一遍,服务器以为这是一次全新的合法调用,于是再次执行扣款、发券、生成Token等操作。 在API世界里,银行柜台就是你的后端服务,取款单就是HTTP请求。防御回放攻击,本质上是给每张“取款单”加上防伪编号、有效期、私章、受理登记簿和监控探头。本文将从攻击原理说起,一步步理解Nonce、时间戳、签名验证、请求去重、异常检测以及多层防御策略,带你为AI服务的API筑起一道完整的防重放大坝。
对AI从业者而言,掌握回放攻击防御是构建安全可靠AI服务的基础功——无论是模型调用接口、数据管道还是Agent工具链,都随时可能与这类攻击正面相遇。
目录
可视化演示
动态展示API回放攻击的五种防御层:Nonce随机数、Timestamp时间窗、HMAC签名、请求去重和异常检测,切换攻击类型查看不同防御层如何拦截各类回放攻击。
本节配有交互动效(进入视口后自动加载,约 3KB)。
精确重放攻击防御 Nonce一次性随机数检测到重复请求,直接拦截 请求去重层记录已处理Nonce,二次提交自动拒绝 replay(request) = reject if nonce in cache 精确重放:Nonce+去重双重拦截
攻击原理:被重放的合法请求
银行保安小王最近发现一件怪事:有位客户明明刚取过钱,几分钟后又拿着同一张取款单回来取了一遍。柜员核对单据时发现签名和日期都对得上,于是又给了一遍钱。直到月底对账时,才察觉这位客户根本不知情——是小偷从垃圾桶里翻出了那张用过的取款单,原样递进了柜台。这就是回放攻击的现实版本:攻击者不破解密码、不篡改内容,只是把一份真实合法的请求原样重发。
在API安全语境里,取款单就是HTTP请求报文。攻击者通过中间人代理、日志泄露或客户端劫持,把一份真实请求完整截获下来,然后对着同一个接口重复发送。服务器端没有“记性”,无法区分这是新请求还是旧请求的复制品,于是重复执行了扣款、开卡、授权等操作。AI服务尤其脆弱:大模型接口按Token计费,一次重放可能就是一笔不小的成本损失。
回放攻击的危害有两个特征:一是隐蔽,因为它不改变请求内容,排查时很难从报文本身发现问题;二是放大,一次截获可以无限重放,直到服务端察觉。攻击者会优先盯上那些高价值、幂等性差的接口——支付、转账、发券、AI计费,这些接口每重放一次就产生一次真实损失。评估风险时,不能只看单次损失,还要看所有可被重放接口的叠加效应:
所以,防回放的核心思路不是阻止攻击者“捡到”请求,而是让他捡到之后用不出去——让任何请求都只能在特定时间、被处理一次。这就是接下来几层防御要做的事。
- loss回放攻击给系统造成的总损失,可以理解成“被冒领的总金额”。
- impact_i第 i 类接口被回放一次带来的直接经济损失,比如一次AI调用扣费。
- P(replay_i)第 i 类接口被成功回放的概率,取决于是否有防重放机制覆盖。
- 求和符号 Σ把各接口的风险加总,得到整个API面的风险敞口。
Nonce防御:一次性防伪单号
银行的单据印刷厂改进了工艺:每张取款单都印上一个唯一的取款编号,柜员在受理时先翻登记簿,如果发现这个编号已经出现过,就当场把单据退回去。这样一来,小偷就算拿到旧单据,也会因为编号重复而露馅。这个编号,就是我们说的Nonce(Number used once)。
Nonce的设计原则很直接:客户端每次发起请求时,生成一个全局唯一的随机字符串(例如UUID),随请求一起发送;服务端接收到请求后,先检查这个Nonce是否已经存在于“已使用集合”中——如果存在,说明这是重复请求,直接拒绝;如果不存在,则把Nonce记入集合,再放行。
Nonce的生成策略通常有三种:基于UUID的随机串、基于“时间戳+随机后缀”的组合、以及基于计数器与随机数混合的算法。随机性越强越难被预测,所以强烈推荐使用密码学安全随机数(如Python的 secrets.token_hex)。服务端存储已使用Nonce时,可以用Redis的 SETNX 命令:只在键不存在时写入成功,天然自带“唯一性检查”;同时给Nonce设置合理的TTL,例如与时间戳窗口保持一致,定期清理,避免存储无限膨胀。Nonce有效性的核心判断很简单:
Nonce是防御回放的第一块基石,但它有个弱点:如果服务端需要保存所有使用过的Nonce,存储成本会随请求量线性上涨。所以Nonce通常不能单独使用,而要和时间戳配合——先把绝大多数“旧过头”的请求挡在外面,Nonce只需在短时间窗口内保证唯一即可。
- nonce客户端本次请求携带的一次性随机数,相当于取款单上的临时编号。
- S_used服务端登记的“已使用Nonce集合”,相当于柜员的取款编号登记簿。
- ∉数学上的“不属于”符号,判断新Nonce是否从未出现过。
- 整体判断当 Nonce 不在已用集合中时,请求才被放行,否则直接拒绝。
时间戳验证:取款单上的填单日期
银行又加了一条规矩:取款单上的填单日期必须与当前日期相差不超过三天,超过三天柜员直接拒收。理由很朴素——真正来取钱的人不会拿着一张上周的单子跑来。时间戳验证就是给API请求贴一张“有效期标签”。
客户端在请求头中携带本地生成时间戳 t_req(Unix毫秒或秒级),服务端收到后计算它与当前时间 t_now 的差值。如果差值超过预设的窗口阈值 ΔT,就认为请求已经“过期”,拒绝处理。窗口大小是个需要权衡的参数:设得太短,客户端和服务器的时钟稍有偏差就会误伤正常请求;设得太长,攻击者在窗口内重放的窗口就越大。业界常见做法是5~10分钟,并配合NTP时间同步来缩小两端时钟漂移。
时间戳验证的计算成本几乎为零,因此非常适合放在防御链路的最前端,先用它过滤掉绝大部分“一眼假”的过期请求,再让残余流量进入Nonce等后续检查。判断逻辑可以表达为:
时间戳验证能挡住“隔了很久才重放”的攻击,却挡不住窗口内的快速重放——攻击者截获请求后立刻重发,时间差仍在阈值内。所以时间戳之后,还要靠Nonce和签名来构造更结实的防线。
- t_now服务器当前时间,相当于柜员看墙上挂钟的读数。
- t_req请求中携带的客户端时间戳,相当于取款单上的填单日期。
- 绝对值 |……|同时容忍客户端时间超前或滞后的情况,防止“时钟逆行”被利用。
- ΔT允许的时间窗口阈值,比如300秒,窗口之外一律拒绝。
签名验证:预留印鉴核对
银行柜台旁多了一本“预留印鉴册”。客户开卡时留下自己的私章印模,取款时必须在单据上盖同样的章,柜员当场比对,不一致就直接拒绝。这个章,就是API请求的数字签名。
签名验证的机制是:客户端和服务端预先共享一个密钥 k_secret,客户端将请求中的关键要素——HTTP方法、请求路径、请求体、时间戳、Nonce——拼成一个字符串,用HMAC-SHA256算法结合密钥算出签名,附在请求头中。服务端收到后,用同样的密钥和同样的拼接规则重新计算一遍签名,若两个签名不一致,说明请求在传输途中被篡改过,或请求者根本没有合法密钥,直接拒绝。
签名把“内容可信”和“身份可信”绑在了一起。攻击者即使截获了完整请求,只要不知道密钥,就无法伪造一个新签名;哪怕他只篡改请求体中的一个小字段,签名也会对不上,服务端会立刻识别出来。密钥管理同样重要:密钥应放在KMS或环境变量中,禁止硬编码进代码仓库;密钥要定期轮换,轮换期间保留新旧两套签名做灰度。签名计算可表示为:
有了签名,攻击者即使捡到完整的取款单,也盖不出第二张一模一样的章——除非他偷走了密钥本身。但签名验证的是“请求是否被信任方签发”,无法识别“同一个合法请求被原样提交两次”。所以,签名还要叠加上去重能力,才能构成闭环。
- k_secret客户端与服务端共享的密钥,相当于预留印鉴的底样,只有双方知道。
- m + p + bHTTP方法(GET/POST)、请求路径和请求实体,把“干了什么”锁进签名里。
- t + n时间戳和Nonce也被拼入签名,让签名与请求“绑定时效”,旧请求换了时间戳就验不过。
- HMAC-SHA256带密钥的哈希算法,输出固定长度的签名字符串,不可逆且防碰撞。
请求去重:柜员受理登记簿
柜员桌上多了一本厚厚的“已受理登记簿”。每笔成功的取款,柜员会把单据内容快速登记一下。如果下一张单据的内容和刚才那笔几乎一模一样——同一客户、同一金额、同一日期——柜员就会警觉起来:这恐怕不是巧合,而是有人拿着同款单据来“二刷”。请求去重做的正是这件事:把已经处理过的请求“记住”,避免相同请求被重复执行。
去重的基本手法是为请求计算一个指纹:把方法、路径、请求体和时间戳拼接后做SHA256哈希,得到一个定长的指纹字符串。服务端在处理请求前,先看这个指纹是否已在缓存中;如果存在,说明业务逻辑已经用过这个请求,直接返回“重复请求”错误;如果不存在,就存入缓存并放行。缓存可以是一张Redis表,也可以调用方标识 + 幂等号(Idempotency-Key)来实现。
请求去重的“记性”不需要永久保存,只要覆盖时间戳窗口的时长即可。比如窗口是5分钟,那么去重缓存的TTL也设为5分钟,之后自然过期,让位于新请求。这种方式既是防回放的最后一道“同内容拦截网”,也是实现接口幂等性的通用手段——即使Nonce被极端情况绕过,同样的请求体也无法再触发第二次业务动作。指纹的生成方法是:
登记簿不是万能的。如果攻击者重放时稍微改一下请求体——比如给JSON加一个无意义的空格——指纹就会变化,去重就可能失效。这正是为什么签名、Nonce、时间戳几道防线必须配合使用:登记簿负责兜底识别“几乎相同”的请求,而签名负责惩罚“任何微小的改动”。
- f请求指纹,相当于登记簿上的一行记录,用于快速识别重复单据。
- m + p + b方法、路径和请求体,组合起来唯一定义“这次请求做了什么”。
- t时间戳加入指纹,让同一操作在不同时间窗口生成不同的指纹,避免误杀合理重复操作。
- SHA256密码学哈希函数,把任意长度的请求内容压缩成64位十六进制指纹。
异常检测:监控探头巡查
银行的安保系统升级了:除了柜台的条条框框,大厅天花板还装了多个监控探头,后台算法会分析每一笔取款的特征——同一个人一天来几次、取款金额是否符合其历史习惯、同一张单据编号是否反复出现。一旦某个指标超出正常范围,系统就会在监控屏上亮起红灯。异常检测,就是从“行为模式”的视角发现那些悄悄发生的回放攻击。
对于API服务来说,异常信号比比皆是:某个AppKey的请求频率突然飙升到平时的几百倍;同一个Nonce在短时间内连续出现多次;请求携带的时间戳高度集中在一个狭窄区间(说明攻击者在一瞬间批量重放);签名验签失败率突然从0.1%涨到20%……这些信号单独看也许不算铁证,但组合起来就非常可疑。
工程实现上,可以为每个调用方维护一个滑动窗口计数器,记录每分钟请求量、失败次数等指标,与一周内的历史基线做对比,计算偏离倍数。当综合异常分数超过阈值时,触发告警并自动执行限流、封禁或人工复核。同时,所有请求的完整链路日志要落盘,包括时间戳、Nonce、签名结果、指纹信息,便于事后审计时还原攻击者的完整轨迹。一个简化版的异常评分可以这样表达:
监控探头最重要的价值,是捕捉那些绕过既定规则的攻击。规则总有盲区,而异常行为模式往往在规则还没反应过来之前就露出了马脚。不过,任何单一检测算法都有误报和漏报的权衡,所以实践中它总是作为“兜底感知层”,与前面的硬校验协同作战。
- r_freq当前请求频率与正常基线的比值,比如平时每分钟50次,现在5000次,比值为100。
- r_fail近期防御层拒绝率,例如验签失败、Nonce重试、时间戳过期的请求占比。
- w1、w2权重系数,业务上可以调节,例如更看重频率异常时把 w1 调大。
- score综合异常分数,超过阈值就触发告警、封禁或一键降级。
多层防御:从保安到金库的纵深防线
回顾整座银行的安保体系:门口有保安验证来客身份,柜台有柜员核验单据编号、日期和私章,登记簿记录每一笔受理,金库有最后一道生物锁,监控中心则盯着每一个角落。这层层关卡不是各自孤立的存在,而是一条纵深防线——攻击者想偷走钱,必须同时骗过所有关卡。这就是多层防御思想在API安全中的落地。
在实际部署中,各层防御的顺序值得讲究:应该把计算成本最低、拒绝率最高的校验放在前面,让可疑请求在最外层就被拦截,避免浪费后续的高成本计算。典型的顺序是:时间戳验证 → Nonce查重 → 签名验签 → 请求去重 → 异常检测。前四层是“硬规则”,直接拒绝不合规请求;最后一层是“软感知”,持续监控整体态势。任何一层都不能单独保证绝对安全,但叠加之后,攻击者的成功率呈指数级下降。假设每层防御的拦截概率为 p_i,则整体拦截概率为:
纵深防御还意味着“允许部分组件降级”。比如异常检测模块依赖的时序数据库出了故障,不能因此导致所有请求失败——此时的策略是跳过检测层并发出警报,保留前四层硬规则继续工作。安全运维中还要定期做攻击演练:用真实的回放攻击脚本去测试每一层防御,找出薄弱环节并加固。记住:防御的目标不是让所有攻击“到不了”系统,而是让攻击者就算捡到取款单,也永远无法凭它走进金库的大门。
- p_i第 i 层防御单独拦截攻击的概率,比如时间戳层能拦下80%的旧请求。
- (1 - p_i)该层防御“漏过攻击”的概率,比如时间戳层漏过20%。
- 连乘 ∏所有层都同时漏过攻击的概率,层数越多,这个值越小。
- 1 - ∏“至少有一层拦住攻击”的概率,即多层叠加后的真实防护率。
AI 应用场景
AI大模型API网关防重放
阶段:API网关安全防护
解决的问题:大模型API按Token计费,回放攻击会让攻击者用截获的合法请求反复调用模型接口,导致企业账单被恶意刷爆。传统基础认证只能识别身份,无法识别“同一个已验证请求被重复发送”。
具体案例:某公司上线了企业级LLM服务,攻击者通过内部日志泄露截获了一个合法的Chat API请求,随后在1小时内重放数千次,当日Token费用暴涨几十倍。部署时间戳+Nonce+签名三层防护后,所有重放请求在网关层被拦截,账单恢复平稳。
AI Agent工具调用链路防重放
阶段:Agent工具调用安全
解决的问题:AI Agent在自动化执行任务时会调用支付、发券、数据写入等工具接口。如果Agent发出的某个关键请求被截获并重放,可能导致用户被重复扣款、重复发券,引发资损事故。
具体案例:一个自动缴费Agent每次提交缴费单时生成一次性Nonce并附带签名;服务端对缴费单请求体计算指纹并缓存5分钟。某次攻击者截获请求尝试重放,被Nonce查重直接拒绝,后续连续试探又触发了异常检测告警,运营人员及时封禁了攻击者IP。
AI模型训练数据接口防重放
阶段:数据管道安全
解决的问题:模型训练和推理链路中,数据标注、特征获取等内部API默认信任内网调用,一旦被内网渗透者截获请求并重放,会造成数据重复拉取、标注任务重复执行。
具体案例:某数据平台为内部标注API启用了基于HMAC签名的调用认证,每个标注任务请求绑定时间戳与Nonce,数据管道运行半年内成功拦截了多起异常重复拉取事件,审计日志清晰还原了每一次攻击尝试。
Python 演示
import hashlibimport hmacimport time class ReplayGuard: """API防回放攻击守卫:时间戳 + Nonce + 签名 + 去重""" def __init__(self, secret, window_sec=300, max_nonce=10000): self.secret = secret self.window = window_sec self.nonce_store = {} self.fingerprints = {} self.fail_count = 0 self.max_nonce = max_nonce def sign(self, method, path, body, timestamp, nonce): message = '{}|{}|{}|{}|{}'.format(method, path, body, timestamp, nonce) return hmac.new(self.secret.encode(), message.encode(), hashlib.sha256).hexdigest() def clean_expired(self, store): now = time.time() return {k: ts for k, ts in store.items() if now - ts <= self.window} def verify_timestamp(self, timestamp): return abs(time.time() - timestamp) <= self.window def verify_nonce(self, nonce): if nonce in self.nonce_store: return False self.nonce_store[nonce] = time.time() if len(self.nonce_store) > self.max_nonce: self.nonce_store = self.clean_expired(self.nonce_store) return True def verify_signature(self, method, path, body, timestamp, nonce, sig): expected = self.sign(method, path, body, timestamp, nonce) return hmac.compare_digest(expected, sig) def fingerprint(self, method, path, body, timestamp): raw = '{}{}{}{}'.format(method, path, body, timestamp) return hashlib.sha256(raw.encode()).hexdigest() def check(self, req): if not self.verify_timestamp(req['timestamp']): self.fail_count += 1 return 'reject:expired' if not self.verify_nonce(req['nonce']): self.fail_count += 1 return 'reject:dup_nonce' if not self.verify_signature(req['method'], req['path'], req['body'], req['timestamp'], req['nonce'], req['sig']): self.fail_count += 1 return 'reject:bad_sig' fp = self.fingerprint(req['method'], req['path'], req['body'], req['timestamp']) if fp in self.fingerprints: self.fail_count += 1 return 'reject:dup_req' self.fingerprints[fp] = time.time() return 'allow' if __name__ == '__main__': guard = ReplayGuard('my-secret-key') ts = int(time.time()) nonce = 'x1f3a9' sig = guard.sign('GET', '/api/balance', '', ts, nonce) req = {'method': 'GET', 'path': '/api/balance', 'body': '', 'timestamp': ts, 'nonce': nonce, 'sig': sig} print('1st:', guard.check(req)) print('replay:', guard.check(req)) old_req = dict(req) old_req['timestamp'] = ts - 600 old_req['nonce'] = 'z9b2c7' old_req['sig'] = guard.sign('GET', '/api/balance', '', old_req['timestamp'], old_req['nonce']) print('expired:', guard.check(old_req)) tamper_req = dict(req) tamper_req['path'] = '/api/transfer' tamper_req['nonce'] = 'q7w1e4' tamper_req['sig'] = guard.sign('GET', '/api/balance', '', ts, tamper_req['nonce']) print('tampered:', guard.check(tamper_req))
guard = ReplayGuard('my-secret-key')初始化防御守卫,传入共享密钥,窗口默认5分钟。sig = guard.sign('GET', '/api/balance', '', ts, nonce)用共享密钥对请求要素生成HMAC签名。if not self.verify_timestamp(req['timestamp']):第一道防线,过期请求直接拒绝。if not self.verify_nonce(req['nonce']):第二道防线,Nonce出现过说明是重复请求。self.nonce_store[nonce] = time.time()记录本次Nonce及到达时间,后续请求查重会用到。hmac.compare_digest(expected, sig)恒定时间比较签名,避免时序侧信道攻击。print('replay:', guard.check(req))同一个请求重放第二次,会被Nonce查重拦截。
常见误区
误区Nonce已经能保证每个请求只被处理一次,为什么还要额外加时间戳?这不是重复建设吗?我只用Nonce就够了。
正解Nonce需要服务端存储所有历史Nonce才能判断“是否见过”,否则无法查重。如果不加时间戳,服务端就必须无限期保存Nonce,存储成本随请求量线性增长。加了时间戳后,绝大多数“旧请求”在Nonce查重之前就被过滤了,Nonce只需在5分钟窗口内唯一即可,存储量大幅下降。就像银行既要编号又要日期:编号是为了查重,日期是为了快速扔掉过期单据。
为何常见:初学者看到Nonce“一次一密”的特性,容易忽略存储成本这一工程现实。
误区请求只要在时间窗口内,又带合法时间戳,不就可以放心了吗?窗口设短一点,比如1秒,攻击者根本来不及重放。
正解时间戳窗口内依然存在重放窗口。攻击者截获请求后立即重放,1秒的窗口也拦不住毫秒级的自动脚本。时间戳只能过滤“隔了很久的重放”,无法识别“刚刚被抓包后又立刻重发的请求”。所以时间戳必须与Nonce、签名、去重组合使用,它只是最外层的廉价过滤器,而不是终极防线。
为何常见:人们天然认为“时间新鲜”就等于“真实”,忽略了自动工具的速度远超人脑感知。
误区签名的作用就是保证请求内容不被篡改,内容没被改就能放行。我检查一下签名对不对,就能防回放了。
正解签名的确能防篡改,但防回放的关键在于签名绑定了哪些字段。如果签名只包含请求体,攻击者原样重放,签名依然有效。只有把时间戳和Nonce也纳入签名计算,旧请求被修改时间戳重放时签名验不过、原样重放时Nonce查重失败,才算真正防回放。签名如同私章,光有章还不够,还要看章盖在哪份单子上。
为何常见:把“完整性保护”与“新鲜性保护”混为一谈,忽略了签名绑定字段范围的设计决策。
误区每加一层防御都要多几次哈希计算和存储访问,会不会把API延迟拖垮?还是少用几层吧。
正解多层的代价其实很可控。时间戳校验是纯内存比较,几乎零成本;Nonce查重是Redis键值操作,亚毫秒级;签名验签虽要一次HMAC计算,但也只有微秒级开销。合理的分层顺序反而能节省成本:最便宜的校验放最前面,让不合法请求在最外层就止步,根本走不到昂贵的业务逻辑。真正昂贵的不是防御层,而是被攻击打穿的业务灾难。
为何常见:凭直觉估算性能开销,没有理解“前置廉价校验”的设计哲学。
面试题
Q1. 以下哪项最符合API回放攻击的本质?
- A.破解用户登录密码
- B.篡改请求中的业务字段
- C.重发截获的合法请求← 正确答案
- D.发送超大流量耗尽带宽
答案:C。回放攻击是把截获的合法请求原样重发,不做任何修改,让服务器误以为这是新的合法调用。破解密码属于暴力破解,篡改字段属于注入或篡改攻击,超大流量则是DDoS,都不符合“重放”的定义。
Q2. Nonce在防回放中的核心作用是什么?
- A.对请求体进行加密
- B.标记请求只能被使用一次← 正确答案
- C.压缩请求的传输体积
- D.替代用户登录令牌
答案:B。Nonce是客户端每次请求生成的唯一随机数,服务端通过记录已使用的Nonce集合来识别重复请求,从而实现“一次性”约束。加密是SSL/TLS的工作,压缩与令牌都不是Nonce的职责。
Q3. 时间戳验证中,窗口阈值设定为5分钟意味着什么?
- A.请求在5分钟后自动重试
- B.请求时间差超过5分钟即被拒绝← 正确答案
- C.服务端每5分钟清理一次日志
- D.客户端每5分钟更换一次密钥
答案:B。窗口阈值 ΔT=5分钟,表示只要服务器当前时间与请求携带时间戳的差值超过5分钟,请求就被视为过期并拒绝。它定义的是新鲜度边界,与重试、日志清理、密钥更换无关。
Q4. HMAC签名中,把时间戳和Nonce纳入签名计算的主要目的是什么?
- A.让签名长度更长更安全
- B.让签名与请求绑定时效和一次性← 正确答案
- C.替代HTTPS的加密通道
- D.降低服务端验签的计算量
答案:B。把时间戳和Nonce纳入签名后,攻击者无法在修改时间戳或替换Nonce后仍保持签名有效,旧请求也不能原样重放。这是让签名参与防回放而非仅仅防篡改的关键设计。
Q5. 以下哪些措施能够有效防御API回放攻击?
- A.时间戳窗口校验← 正确答案
- B.Nonce唯一性查重← 正确答案
- C.增加请求体大小限制
- D.签名绑定时间戳与Nonce← 正确答案
答案:ABD。时间戳窗口过滤过期请求,Nonce查重识别重复请求,签名绑定时间戳和Nonce则让篡改和回放同时失效。请求体大小限制属于资源防护,与回放攻击无关。
Q6. 请求去重通常使用什么来识别“同一请求”?
- A.客户端IP地址
- B.请求体的哈希指纹← 正确答案
- C.HTTP协议版本号
- D.浏览器User-Agent
答案:B。请求去重把方法、路径、请求体、时间戳等字段拼接后计算SHA256指纹,存入缓存比对。IP地址会被NAT混淆,协议版本和User-Agent无法唯一识别请求内容。
Q7. 以下哪些异常信号可能表明API正遭受回放攻击?
- A.同一Nonce在短时间反复出现← 正确答案
- B.请求时间戳高度集中簇聚← 正确答案
- C.签名验签失败率突然飙升← 正确答案
- D.请求体长度保持固定且偏大
答案:ABC。同一Nonce重复出现说明攻击者正在重放旧请求;时间戳集中簇聚说明攻击者在短时间内批量重放抓包数据;验签失败率飙升说明攻击者尝试修改请求但缺少密钥。请求体固定偏大不构成攻击信号,正常文件上传也可能如此。
Q8. 多层防御中,为什么通常把时间戳校验放在第一道防线?
- A.因为它能识别所有攻击类型
- B.因为它是计算成本最低的快速过滤器← 正确答案
- C.因为它比签名验证更准确
- D.因为它不需要客户端配合
答案:B。时间戳校验只需一次时间差的绝对值比较,计算成本几乎为零,却能在最外层过滤掉大量过期重放请求。优先执行廉价高效的校验,避免后续高成本计算被垃圾请求浪费。
Q9. 纵深防御(多层防御)的优点包括哪些?
- A.单层被绕过时其他层仍可拦截← 正确答案
- B.各层拦截概率综合后整体防护率更高← 正确答案
- C.可以完全消除所有误报
- D.无需维护任何日志或监控
答案:AB。多层防御的核心价值在于“不把鸡蛋放在一个篮子里”:单层失效时其余层仍然工作,且各层拦截概率叠加后整体防护率按公式 1−∏(1−p_i) 提升。误报无法完全消除,日志监控更是必要的辅助手段。
Q10. 当Nonce存储迅速膨胀时,最合理的处理方式是什么?
- A.无限扩容Redis存储
- B.定期清理超过时间窗口的过期Nonce← 正确答案
- C.直接关闭Nonce校验改用IP白名单
- D.让所有客户端复用同一个固定Nonce
答案:B。Nonce只需在时间戳窗口内保持唯一即可,超过窗口的Nonce已无查重价值,应通过定期清理(如惰性删除或定时任务)释放存储。无限扩容浪费资源,关闭校验和固定Nonce都会让防御形同虚设。