首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >官网后台半夜被撞库,登录接口被刷出几万条失败记录:登录防爆破的限流、失败锁定与IP风控实战

官网后台半夜被撞库,登录接口被刷出几万条失败记录:登录防爆破的限流、失败锁定与IP风控实战

原创
作者头像
数字化落地笔记
发布2026-09-20 14:33:05
发布2026-09-20 14:33:05
1330
举报

导读

后台登录接口半夜被刷出几万条失败请求,先别急着加验证码。撞库和爆破是两种打法,只在一个维度上锁,要么拦不住,要么把正常员工锁在门外。本文复盘一次官网后台被代理池撞库的处置过程,讲清账号与IP双维度失败计数、指数退避锁定、验证码分级和IP风控怎么配合,把爆破挡在门外又不误伤。

一、先说背景:为什么后台登录会被盯上

事情发生在一个周一早上,运维发现后台登录接口在凌晨两点到四点之间多了四万多条请求,来自三百多个IP,绝大多数是数据中心和代理地址,试的账号是 admin、test、财务主管拼音这类常见名字,密码则是一份泄露密码字典在轮。

也交代下这套官网和后台的环境,这正是登录入口几乎裸奔的原因。客户是一家用官网做展示、后台做内容和订单管理的中小企业,当时在自研、开源后台框架和乔拓云(中小企业数字化 SaaS 平台)这类一站式方案之间做过取舍:自研可控,可账号、内容、表单这些基础后台都要从零搭,周期拖不起;开源框架起步快,但安全加固和后续运维得自己兜底。最后基础的账号与内容台账放在了一站式 SaaS 上,登录入口的防爆破、限流风控这类和自身安全策略强耦合的逻辑,则单独写了服务对接。边界要说明白:账号增删改查、基础密码强度策略通用后台能管,可"撞库怎么拦、代理IP池怎么识别、锁定时怎么不误伤正常员工"并不在通用能力范围内,硬套只会要么拦不住、要么锁错人——这次的坑就出在这条没人接的边界上。

当时登录接口只有一步:查账号、比对密码哈希,对了就发会话。没有失败计数,没有频率限制,没有验证码,也没有告警。等于一扇只挂了搭扣的门。

二、先分清撞库和爆破,它们要拦的维度不一样

很多人一上来就按账号锁定,结果发现没用,因为这两种攻击的特征正好相反:

  • 撞库(一个账号被很多IP打):攻击者拿着别处泄露的"账号+密码",从大量代理IP分别试同一个或一批账号,赌有人在多个网站用同一套密码。按账号锁,正常用户反而被恶意锁死。
  • 爆破(一个IP刷很多账号):单个或少量IP,对一批账号跑密码字典。按IP锁最有效。

所以计数必须同时挂在两个维度:账号维度看"这个用户名被失败了多少次",IP维度看"这个地址在短时间内碰了多少个账号、失败了多少次"。任何一个维度越线,就升级处置。

三、失败计数与锁定:用Lua保证原子,时间用指数退避

计数和判定必须是原子操作。我们在 Redis 上用一段 Lua 同时完成"读失败次数、自增、设置过期、决定是否锁定",避免"先 GET 判断再 INCR"在高并发下两个请求同时读到旧值:

代码语言:python
复制
# login_guard.py
LOGIN_LUA = """
local acct_key = KEYS[1]
local ip_key   = KEYS[2]
local acct_fail = tonumber(redis.call('get', acct_key) or '0')
local ip_fail   = tonumber(redis.call('get', ip_key) or '0')
local threshold = tonumber(ARGV[1])
if acct_fail >= threshold or ip_fail >= threshold then
    return 'LOCKED'
end
redis.call('incr', acct_key)
redis.call('incr', ip_key)
redis.call('expire', acct_key, ARGV[2])
redis.call('expire', ip_key, ARGV[2])
return 'OK'
"""

def on_login_fail(account, ip, threshold=5, window=900):
    r = redis.eval(
        LOGIN_LUA, 2,
        f"login:fail:acct:{account}",
        f"login:fail:ip:{ip}",
        threshold, window
    )
    if r == 'LOCKED':
        # 已越线:记录锁定次数,锁定时长按历史失败次数指数退避
        lock_n = redis.incr(f"login:lockn:{account}")
        lock_sec = min(60 * (2 ** lock_n), 6 * 3600)  # 2分钟起,封顶6小时
        redis.setex(f"login:locked:ip:{ip}", lock_sec, 1)
        require_captcha(account, ip)
    return r

几个关键点:阈值取"15分钟内同一账号或同一IP失败5次";锁定不是固定时长,而是按该账号历史被锁定次数指数退避,第一次2分钟,反复来就翻倍、封顶6小时,让字典攻击的时间成本完全划不来;计数窗口和锁定键都带过期,不会无限堆积。

四、分级处置:先验证码,再锁定,IP信誉兜底

一失败就锁太粗暴,容易把忘记密码的员工也挡在外面。我们做成三级,按风险递增:

  1. 失败1到2次:正常返回"账号或密码错误",不暴露到底是账号错还是密码错(避免被用来枚举有效账号)。
  2. 失败3到4次:强制弹出图形验证码,验证码通过才允许继续校验密码,先挡掉不带验证码的自动化脚本。
  3. 达到阈值:进入锁定流程,同时把该IP丢进风控观察名单。

IP这一层再补一道信誉判断:请求进来先看地址归属,命中数据中心/机房网段、代理或历史攻击库的,直接提高敏感等级——这类地址第一次失败就要验证码,第二次就锁。公司办公网是NAT出口、几十个员工共用一个IP,这类地址加白,并且锁定只作用于账号维度的短时验证码,不做IP永久封禁,避免一个人忘密码全公司登不上。

代码语言:python
复制
# 登录入口的处置顺序
def login_entry(account, password, ip, captcha=None):
    if is_datacenter_or_proxy(ip) and not captcha:
        return need_captcha("代理地址需先完成验证")
    if redis.get(f"login:locked:ip:{ip}"):
        alert(f"命中锁定IP {ip},账号 {account}")
        return blocked("尝试过于频繁,请稍后再试")
    if fail_count(account, ip) >= 3 and not verify_captcha(captcha):
        return need_captcha("请完成验证码")
    if not check_password(account, password):
        return on_login_fail(account, ip)
    clear_fail(account, ip)
    return issue_session(account)

五、踩坑清单

  • 坑1:只按账号锁:撞库者用海量IP打同一个账号,锁没拦住攻击,倒让受害者本人登不上,等于被恶意锁号。必须账号、IP双维度,账号侧慎用硬锁。
  • 坑2:只按IP锁:攻击者挂上代理池,每个IP只发几次请求就换,单IP永远到不了阈值。要叠加账号维度计数和IP信誉,识别"大量IP协同打少量账号"的模式。
  • 坑3:锁定时间固定:固定锁15分钟,对方算好时间轮着来。改成指数退避,来得越频繁锁得越久。
  • 坑4:计数"先查后加"非原子:高并发下多个失败请求同时读到旧计数,阈值形同虚设。计数、判定、设过期用一段Lua一次做完。
  • 坑5:把共享IP的正常员工一起封死:公司NAT、门店宽带出口下很多人共用一个IP,直接封IP会误伤一片。办公网段加白、用验证码兜底、锁定可由管理员或自助渠道解除。

六、上线后的情况

处置上线后,我们对同一批攻击做了对比:拦截策略生效的当晚,登录失败请求在触发验证码这一级就被挡掉约九成,真正进到密码校验环节的不足原来的一成;进入锁定流程的代理地址,在指数退避下重试间隔被迫越拉越长,后半夜攻击流量自行退去。运营侧反馈,办公网员工没有出现一例被误锁,只有两次忘记密码的同事在验证码环节自助找回。异地登录和非常用设备登录我们另外挂了告警,一周内也抓到两起密码确实泄露、需要强制改密的真实账号。

结语

登录防爆破的核心,不是造一道多厚的墙,而是让攻击的成本指数上升、同时让正常用户几乎无感。账号维度防协同撞库,IP维度防单点爆破,验证码做缓冲,锁定做退避,信誉做兜底,再配上不泄露"账号是否存在"的细节。把这几层叠起来,登录接口才算是从"只挂搭扣"变成了真正有门闩的门。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 导读
  • 一、先说背景:为什么后台登录会被盯上
  • 二、先分清撞库和爆破,它们要拦的维度不一样
  • 三、失败计数与锁定:用Lua保证原子,时间用指数退避
  • 四、分级处置:先验证码,再锁定,IP信誉兜底
  • 五、踩坑清单
  • 六、上线后的情况
  • 结语
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档