导读:本期聚焦于弦宿​创作的《如何利用Rack::Attack动态封禁恶意IP并防范暴力破解攻击?》,敬请观看详情。服务器日志里频繁出现来自同一个IP的大量失败登录记录,要不要手动拉黑?Rack::Attack作为Rack中间件层面的限流与封禁工具,能够在请求进入业务逻辑之前完成拦截。本文围绕动态黑名单机制展开,介绍如何通过fail2ban风格失败计数、Redis持久化存储、自动过期解封来构建自愈式防护体系,同时给出针对登录接口、API认证等场景的限流配置示例,分析封禁策略的误伤风险与调优思路,帮助你在Ruby on Rails或Rack应用中低成本实现暴力破解防护。

Rails应用的日志里,每天都能看到成百上千次针对/users/sign_in的POST请求,用户名密码组合五花八门,全部来自几个固定的IP段。手动在Nginx层拉黑显然治标不治本,IP换一批又来了。真正有效的做法是在应用层构建一套动态黑名单:谁在短时间内产生了足够多的失败行为,谁就被自动封禁一段时间,到期自动解除。Rack::Attack正是实现这套机制最顺手的工具。

如何利用Rack::Attack动态封禁恶意IP并防范暴力破解攻击?

Rack::Attack的基础结构与工作原理

Rack::Attack是一个Rack中间件,它工作在请求进入Rails路由和控制器之前的位置。这意味着被它拦截的恶意请求根本不会消耗业务层的资源,不需要实例化控制器、不需要查询数据库,直接在中间件层面就被返回429或403了。对于暴力破解这种高频低成本的攻击,把拦截点前置到中间件层是性价比最高的方案。

它的核心概念有三个:safe_list(白名单)、blocklist(黑名单)和throttle(限流)。黑名单和限流的区别在于:限流统计的是请求次数,超过阈值就临时拒绝;而黑名单可以基于任意条件直接拒绝,条件可以是静态的IP段,也可以是动态变化的,比如Redis里某个键是否存在。动态黑名单正是利用了这一点——当限流规则检测到某个IP的失败登录次数超标时,把它写入Redis,黑名单规则每次请求都检查Redis,命中即拒绝。

p初始化的代码放在config/initializers/rack_attack.rb中,Gemfile里加入gem 'rack-attack'后执行bundle即可。Rails会自动加载这个initializer,中间件链的挂载在新版本中已经默认开启,老版本需要手动config.middleware.use Rack::Attack

构建失败计数与动态封禁机制

光靠限流还不够精细。暴力破解的特征不是请求多,而是失败多,正常的用户可能连续输错两三次密码,但不会一分钟内失败二十次。所以我们需要在业务层记录失败次数,再交给Rack::Attack判断。思路是:登录失败时往Redis里对一个以IP为维度的计数器自增并设置过期时间,Rack::Attack的blocklist规则读取这个计数器,超过阈值就把IP加入封禁集合。

# config/initializers/rack_attack.rb
class Rack::Attack
  # 配置Redis作为存储(默认是进程内存,多进程部署必须换Redis)
  cache.store = ActiveSupport::Cache::RedisCacheStore.new(
    url: ENV.fetch("REDIS_URL", "redis://127.0.0.1:6379/1")
  )

  ### 动态黑名单:检查封禁集合 ###
  blocklist("banned_ips") do |req|
    # 只对登录、注册等敏感路径生效
    if req.path =~ %r{^/(users/sign_in|admin/login)}
      Rack::Attack::BANNED_IPS.include?(req.ip)
    end
  end

  ### 限流规则:登录接口每分钟最多5次请求 ###
  throttle("logins/ip", limit: 5, period: 60) do |req|
    req.post? && req.path == "/users/sign_in"
  end

  class << self
    BANNED_IPS = Set.new

    # 由业务层调用的封禁方法
    def ban_ip!(ip, duration = 30.minutes)
      cache.write("ban:#{ip}", true, expires_in: duration)
      BANNED_IPS << ip
      Rails.logger.warn("[Rack::Attack] banned #{ip} for #{duration}s")
    end

    def banned?(ip)
      cache.read("ban:#{ip}").present?
    end
  end
end

为了让规则真正读Redis而非进程内存集合,blocklist块内应该改为Rack::Attack.cache.read("ban:#{req.ip}").present?这样的动态查询。上面代码保留Set只是为了演示结构,实际生产中封禁状态必须放在Redis里,否则Puma多进程部署时每个进程各有一份内存缓存,封禁会时灵时不灵,这是最常见的一个坑。

业务层的挂钩同样关键。以Devise为例,可以覆写控制器的failed_attempts逻辑,或者在SessionsController#create里rescue认证失败异常,调用Rack::Attack.ban_ip!(request.remote_ip, 30.minutes)。更彻底的做法是结合计数:失败三次触发验证码,失败十次直接封禁半小时,封禁时长随失败次数递增,形成指数退避式的惩罚。

误伤防护、解封策略与生产调优

动态黑名单最大的风险是误伤。大公司出口、校园网、机场WiFi往往成百上千人共享一个公网IP,一个人输错密码可能连累整个网络段被锁。缓解手段有几条:第一,白名单先行,把已知的企业出口IP、健康检查探针IP放进safelist,白名单规则优先级最高,命中直接放行;第二,封禁键值里同时绑定IP和登录目标账号,只针对可疑组合限流,而不是一刀切封IP的所有访问;第三,登录页保留合理的限流阈值,正常用户很难一分钟提交五次表单。

# 白名单:内部网络与监控探针永不拦截
safelist("allow_localhost") do |req|
  req.ip == "127.0.0.1" || req.ip.start_with?("10.0.", "192.168.")
end

# 封禁时长递增:第N次被封,时长为 2^N 分钟
def ban_duration_for(ip)
  count = cache.read("ban_count:#{ip}").to_i
  [2**count, 1440].min.minutes # 最长24小时
end

解封策略上,推荐全部依赖Redis的expires_in自动过期,不要维护手动解封后台,除非安全团队有明确需求。自动过期让封禁具备自愈性,即使攻击者换IP回来,计数器重新累积,又会触发新一轮封禁,整个闭环无需人工介入。如果确实需要人工干预,可以做一个仅管理员可见的接口,删除ban:某IP这个键即可。

最后是可观测性。Rack::Attack提供ActiveSupport::Notifications钩子,订阅rack_attack事件可以把每次拦截记录到日志或监控系统中。建议把封禁触发量做成监控指标,突然飙升往往意味着撞库攻击正在进行,这时候可以临时收紧阈值或延长封禁时长。配合日志里记录的req.ip、命中的规则名,排查误伤也只需要几分钟。整体而言,这套方案零额外基础设施成本,一个Redis加几十行配置,就能挡住绝大多数脚本化的暴力破解。

Rack::Attack黑名单暴力破解防护修改时间:2026-09-06 05:44:35

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260906/51376.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。