使用Rack :: Attack进行消防登录尝试
我们一直在阅读权威指南,以形成基于网站的身份验证,旨在防止快速登录尝试。
其中一个例子可能是:
其他方法出现在指南中,但它们都需要能够记录先前失败尝试的存储。
作为一个可能的解决方案,在本期的其中一篇文章中讨论了分块列表(出现在旧文档黑名单中,文档中更改为分块列表)。
根据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及其存储选择一无所知。
对不起,迟到回到你身边; 我花了一段时间才找出与之相关的旧代码。
正如在上面的评论中所讨论的,这里是一个使用blacklist
和findtime
的解决方案
# 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