把短信轰炸防护和CC攻击防护的黑名单库打通,这件事本身不是技术难点,而是运维策略的升级。多数团队习惯把这两套规则分开维护,WAF管HTTP流量,短信网关管接口调用,各自积累各自的恶意IP和手机号。分开管的后果很直接:同一个攻击者在CC攻击被拦截后,扭头就能用同一组IP资源对短信接口发起轰炸,防御体系出现明显的断层。把黑名单合并共享,本质上是让两个维度的恶意特征数据互相补充,把单点防御变成联动防御。
为什么分开维护的黑名单会失效短信轰炸防护的传统做法是限制单手机号的接收频率、加图形验证码、限制同一IP的请求次数。CC防护则是基于请求速率、会话特征、HTTP头部指纹来做拦截。问题在于,攻击者现在很少只用单一手段。常见的组合策略是先用CC攻击消耗服务器资源,趁运维人员注意力被牵制时,对短信接口发起高频调用。如果两套系统的黑名单不互通,短信网关看不到HTTP层的攻击特征,WAF也不关心短信接口的异常调用,攻击者就能在两个系统之间反复横跳。实际案例中,我们见过同一个C段IP在30秒内触发CC拦截后,立即对短信接口发起了超过200次请求,而短信侧的规则库完全没有任何预警,因为它的黑名单里根本没有这个IP的历史记录。
共享黑名单库的数据结构设计共享黑名单的核心不是简单地把两个表合并,而是要抽象出一层统一的恶意特征模型。建议以IP地址、设备指纹、手机号段三个维度作为主键,每条记录附带一个攻击类型标签数组和最近触发时间戳。IP地址是最直接的关联键,但要注意CDN和运营商NAT场景下的误伤问题,所以IP必须配合设备指纹一起用。设备指纹可以通过前端JS采集浏览器Canvas指纹、WebGL指纹、字体列表等特征生成,即使攻击者切换IP,只要设备指纹不变,依然能命中黑名单。手机号段维度则主要用于识别号段轰炸行为,有些攻击者会批量注册相邻号段的虚拟号码,单个号码的请求频率都不高,但号段维度的聚合指标会明显异常。
数据结构可以参考以下设计思路:
{
"ip": "192.168.1.100",
"device_fingerprint": "a1b2c3d4e5f6",
"phone_prefix": "1380013",
"attack_tags": ["cc_flood", "sms_bomb"],
"first_seen": 1715040000,
"last_seen": 1715043600,
"hit_count": 47,
"block_expire": 1715126400,
"source": "waf_rule_engine"
}
这个结构里,attack_tags数组可以同时包含cc_flood和sms_bomb两种标签,意味着同一个实体无论触达哪个系统,另一个系统都能立即读取到完整的攻击历史。block_expire字段用于设置自动过期时间,避免黑名单无限膨胀。source字段记录首次触发拦截的来源系统,方便后续做归因分析。
实时同步机制的技术选型两个系统共享黑名单,同步延迟是关键指标。如果CC防护拦截了一个IP,30秒之后短信网关才收到同步,这30秒的空窗期足够攻击者完成一轮轰炸。建议采用Redis作为共享存储层,WAF和短信网关都直连同一个Redis集群,拦截事件写入时直接执行SET命令,查询时用MGET批量拉取。对于跨机房部署的场景,可以用Redis Sentinel或Cluster模式保证高可用,同步延迟通常控制在毫秒级。
如果团队已经有统一的配置中心或服务网格,也可以把黑名单查询封装成gRPC接口,由专门的策略服务负责维护内存缓存和持久化。这种方案的优点是可以在查询逻辑里加入更复杂的判断,比如根据attack_tags的权重计算综合风险分数,而不是简单的是非判断。但代价是增加了一次RPC调用,需要在短信接口的请求链路上做好超时控制和降级策略,策略服务挂了不能阻塞正常业务请求。
降低误伤率的几个关键策略黑名单共享之后,最大的风险不是漏报,而是误伤。WAF的CC规则有时候会比较激进,一个公司出口IP因为某位员工误操作触发了CC拦截,如果这个IP被同步到短信黑名单,可能导致整个公司的用户都收不到验证码。解决这个问题需要分层处理:WAF侧触发的IP拦截,在同步到短信黑名单时应该附加一个置信度标记,短信侧根据自身业务场景做二次判断。
具体做法可以设置三个风险等级。高危等级直接拒绝,比如30秒内触发过两种以上攻击类型的IP,或者设备指纹关联了超过5个被标记的手机号。中危等级触发验证码升级,比如原本是滑动验证,升级为点选验证或语音验证。低危等级只记录日志不拦截,用于积累数据和后续分析。风险等级的划分规则需要根据实际业务数据持续调优,初期可以保守一些,宁可放过少量攻击,也不能大规模误伤正常用户。
另一个有效手段是白名单机制。企业内部IP、合作方服务器IP、主流运营商网关IP都应该加入白名单,白名单内的实体不参与黑名单同步,也不受黑名单规则约束。白名单的维护最好做成动态的,通过定时任务从CMDB或资产管理系统自动同步,避免人工维护导致遗漏。
手机号维度的特殊处理逻辑短信黑名单里手机号是最敏感的维度,因为手机号直接关联用户身份,一旦误伤,用户完全无法完成注册或登录,客服压力会瞬间暴增。建议对手机号维度的拦截采用更谨慎的策略:单个手机号触发短信轰炸规则后,只对短信接口生效,不同步到CC防护侧。反过来,CC防护侧拦截的IP,同步到短信侧时,不应该直接关联到该IP下请求过的手机号,因为同一IP下可能有大量正常用户。
手机号的封禁时长也需要差异化设置。IP和设备指纹可以封禁24小时甚至更长,但手机号建议首次触发只封禁5到10分钟,第二次触发封禁30分钟,第三次再升级到24小时。这种阶梯式封禁能有效打断自动化攻击脚本的节奏,同时给误伤用户留出申诉和恢复的时间窗口。封禁期间用户联系客服,可以通过人工审核的方式提前解封,解封操作需要同步清理Redis里的黑名单记录。
日志审计和攻击溯源能力黑名单共享之后,日志体系必须跟上,否则出了问题很难排查。每条拦截记录都应该包含完整的上下文信息:触发时间、触发系统、命中的规则ID、请求的原始Payload摘要、最终执行的动作。这些日志建议统一输出到Elasticsearch或ClickHouse,方便做聚合分析。攻击溯源时,可以通过设备指纹串联起同一个攻击者在不同时间、不同IP下的所有行为,还原完整的攻击路径。
日志里有一个容易被忽略的细节:记录拦截动作的发起方和响应方。比如WAF拦截了一个IP并写入共享黑名单,这条写入操作本身也需要记录日志,包括写入的时间、写入的字段内容、写入是否成功。后续如果短信侧因为这条黑名单拦截了某个请求,日志里应该关联回WAF侧的原始拦截记录ID,形成完整的证据链。这套日志体系对于应对安全审计和合规检查也很有价值。
运维监控和自动化告警共享黑名单库的健康状态需要纳入日常监控。关键指标包括:黑名单条目总数、每分钟新增条目数、每分钟命中次数、同步延迟P99值、Redis内存占用率。当新增条目数突然飙升时,可能意味着正在遭受大规模攻击,也可能是某条规则出现了误判。设置阈值告警时,建议用环比数据而不是绝对值,比如新增条目数超过前5分钟平均值的3倍时触发告警,这样能适应不同时段的流量波动。
另外需要监控的是黑名单命中率的变化趋势。如果命中率突然下降,可能意味着攻击者更换了IP池或设备指纹策略,防御规则需要同步更新。如果命中率持续偏高,则需要检查是否存在规则过于宽泛导致的误伤。这些监控数据最好做成可视化看板,安全运维人员能直观地看到两个系统的联动效果。
实施落地的分阶段建议不建议一步到位把所有维度都打通。第一阶段先共享IP黑名单,跑通数据同步链路,观察一到两周的误伤率和拦截效果。第二阶段引入设备指纹,这个阶段需要前端配合部署指纹采集脚本,同时要处理好指纹缺失的降级逻辑,不是所有客户端环境都能成功生成指纹。第三阶段再考虑手机号维度的有限共享,只共享明确的号段攻击特征,不共享单个手机号。每个阶段都要有完整的回滚方案,一旦出现大规模误伤,能快速切回独立黑名单模式。
代码层面,建议在短信接口的中间件里增加一个黑名单查询模块,伪代码逻辑大致如下:
func SmsRateLimitMiddleware(ctx context.Context, req *SmsRequest) error {
ip := GetClientIP(ctx)
fingerprint := GetDeviceFingerprint(ctx)
phone := req.PhoneNumber
phonePrefix := phone[:7]
keys := []string{
fmt.Sprintf("bl:ip:%s", ip),
fmt.Sprintf("bl:fp:%s", fingerprint),
fmt.Sprintf("bl:prefix:%s", phonePrefix),
}
records, err := redis.MGet(ctx, keys...)
if err != nil {
// Redis不可用时降级为本地内存黑名单
return localBlacklist.Check(ip, phone)
}
riskLevel := EvaluateRisk(records)
switch riskLevel {
case HIGH:
return ErrBlocked
case MEDIUM:
return UpgradeCaptcha(ctx)
default:
return nil
}
}
这个中间件的设计要点是Redis查询失败时的降级策略,以及风险等级的实时计算逻辑。EvaluateRisk函数需要综合考虑多个维度的命中情况和时间衰减因子,不能简单粗暴地只要命中就拦截。
共享黑名单这件事做到位之后,收益会超出预期。不仅仅是拦截率提升,更重要的是安全运维成本的降低。以前需要两拨人分别维护两套规则,现在一套规则库就能覆盖两个场景,策略调整的响应速度也更快。而且随着黑名单数据的积累,还能基于历史攻击特征训练出更智能的预测模型,从被动响应逐步转向主动防御。
