使用Rack :: Attack进行消防登录尝试

我们一直在阅读权威指南,以形成基于网站的身份验证,旨在防止快速登录尝试。

其中一个例子可能是:

  • 1次失败尝试=没有延迟
  • 2次失败尝试= 2秒延迟
  • 3次失败尝试= 4秒延迟
  • 等等
  • 其他方法出现在指南中,但它们都需要能够记录先前失败尝试的存储。

    作为一个可能的解决方案,在本期的其中一篇文章中讨论了分块列表(出现在旧文档黑名单中,文档中更改为分块列表)。

    根据Rack :: Attack的具体情况,一个天真的实现例子可能是:

    登录失败的地方:

    StorageMechanism.increment("bad-login/#{req.ip")
    

    在rack-attack.rb中:

    Rack::Attack.blacklist('bad-logins') { |req|
        StorageMechanism.get("bad-login/#{req.ip}")
    }
    

    这里有两部分,如果它被列入阻止列表,则返回响应并检查是否发生了先前的失败尝试(StorageMechanism)。

    第一部分,返回响应,可以由宝石自动处理。 但是,我没有看到第二部分那么清楚,至少在事实上选择了用于创业板和Rails世界的缓存后端Redis。

    据我所知,Redis中过期的密钥会自动删除。 这将无法访问信息(即使过期),为计数器设置一个新值,并相应增加不应期的超时值。

    有没有办法通过Redis和Rack :: Attack实现这一点?

    我在想,也许'StorageMechanism'在这种情况下必须保持绝对的不可知性,并且对Rack :: Attack及其存储选择一无所知。


    对不起,迟到回到你身边; 我花了一段时间才找出与之相关的旧代码。

    正如在上面的评论中所讨论的,这里是一个使用blacklistfindtime的解决方案

    # config/initilizers/rack-attack.rb
    class Rack::Attack
      (1..6).each do |level| 
        blocklist("allow2ban login scrapers - level #{level}") do |req| 
          Allow2Ban.filter( 
            req.ip, 
            maxretry: (20 * level), 
            findtime: (8**level).seconds, 
            bantime: (8**level).seconds 
          ) do 
            req.path == '/users/sign_in' && req.post? 
          end 
        end 
      end
    end
    

    您可能希望根据您的特定应用需要调整这些数字; 上述数字只是我认为对我的特定应用“明智”的 - 它们不是来自任何官方标准。

    在开发/测试(例如你的rspec测试套件)应用程序时,使用上述的一个问题是,你可以轻松地达到上述限制并无意中扼杀自己。 这可以通过将以下配置添加到初始化程序来避免:

    safelist('allow from localhost') do |req|
      '127.0.0.1' == req.ip || '::1' == req.ip
    end
    

    最常见的暴力登录攻击是一种暴力密码攻击,攻击者只需尝试大量电子邮件和密码以查看是否有任何凭证匹配。

    在几次失败的登录尝试后,您应该通过使用帐户LOCK来减轻应用程序的负担。 (例如,如果使用devise那么您可以使用内置的可Lockable模块。)

    但是,这种帐户锁定方法会打开新的攻击媒介:攻击者可以通过使用有效的电子邮件和不正确的密码进行登录尝试来垃圾邮件系统,以不断重新锁定所有帐户!

    通过指数限制来自给定IP的登录尝试次数,此配置有助于缓解攻击向量。

    我还添加了以下“全部”请求限制:

    throttle('req/ip', limit: 300, period: 5.minutes, &:ip)
    

    这主要是为了遏制恶意/糟糕配置的刮板; 以防止他们占用所有应用程序服务器的CPU。

    注意:如果您通过机架服务资产,这些请求可能会被机架攻击计算在内,并且此节流可能会被激活得太快。 如果是这样,请启用该条件以将其排除在跟踪之外。


    我还编写了一个集成测试,以确保我的Rack::Attack配置正在完成其工作。 在做这个测试工作时遇到了一些挑战,所以我会让代码+评论自己说话:

    class Rack::AttackTest < ActionDispatch::IntegrationTest 
      setup do 
        # Prevent subtle timing issues (==> intermittant test failures) 
        # when the HTTP requests span across multiple seconds 
        # by FREEZING TIME(!!) for the duration of the test 
        travel_to(Time.now) 
    
        @removed_safelist = Rack::Attack.safelists.delete('allow from localhost') 
        # Clear the Rack::Attack cache, to prevent test failure when 
        # running multiple times in quick succession. 
        # 
        # First, un-ban localhost, in case it is already banned after a previous test: 
        (1..6).each do |level| 
          Rack::Attack::Allow2Ban.reset('127.0.0.1', findtime: (8**level).seconds) 
        end 
        # Then, clear the 300-request rate limiter cache: 
        Rack::Attack.cache.delete("#{Time.now.to_i / 5.minutes}:req/ip:127.0.0.1") 
      end 
    
      teardown do 
        travel_back # Un-freeze time 
        Rack::Attack.safelists['allow from localhost'] = @removed_safelist 
      end 
    
      test 'should block access on 20th successive /users/sign_in attempt' do 
        19.times do |i| 
          post user_session_url 
          assert_response :success, "was not even allowed to TRY to login on attempt number #{i + 1}" 
        end 
    
        # For DOS protection: Don't even let the user TRY to login; they're going way too fast. 
        # Rack::Attack returns 403 for blocklists by default, but this can be reconfigured: 
        # https://github.com/kickstarter/rack-attack/blob/master/README.md#responses 
        post user_session_url 
        assert_response :forbidden, 'login access should be blocked upon 20 successive attempts' 
      end 
    end
    
    链接地址: http://www.djcxy.com/p/21619.html

    上一篇: fire login attempts with Rack::Attack

    下一篇: Keeping track of failed login attempts