Rack-Attack 是一个用于防止 Web 应用程序受到恶意攻击的 Ruby gem。它可以轻松集成到任何使用 Rack 的 Ruby 应用程序中,例如 Rails、Sinatra、Hanami 等。本文档将介绍 Rack-Attack 的基本用法和高级功能。
基本用法
安装
要使用 Rack-Attack,你需要在你的 Ruby 应用程序中添加它的 gem。在 Gemfile 中添加以下代码:
gem 'rack-attack'
然后运行 bundle install 安装 gem。
配置
# config/application.rb
config.middleware.use Rack::Attack
限制请求速率
你可以使用 Rack-Attack 来限制某个 IP 地址在一定时间内发送请求的数量。例如,你可以限制某个 IP 地址在 1 分钟内最多只能发送 60 个请求:
Rack::Attack.throttle("requests by ip", limit: 60, period: 60) do |request|
request.ip
end
防止暴力破解
你可以使用 Rack-Attack 来防止暴力破解攻击,例如限制用户在一定时间内最多只能进行几次登录尝试:
Rack::Attack.throttle("logins/ip", limit: 5, period: 20.seconds) do |req|
if req.path == '/login' && req.post?
# return the email if present, nil otherwise
req.params['email'].presence
end
end
上面的代码将限制每个 IP 地址在 20 秒内最多只能进行 5 次登录尝试。如果超过了这个限制,Rack-Attack 将返回一个 429 Too Many Requests 响应。
防止 CSRF 攻击
你可以使用 Rack-Attack 来防止 CSRF 攻击,例如阻止来自外部域名的 POST 请求:
Rack::Attack.blocklist('block POST from external domains') do |req|
req.post? && URI(req.referer || '').host != req.host
end
上面的代码将阻止来自外部域名的 POST 请求,并返回一个 403 Forbidden 响应。
自定义响应
你可以使用 Rack-Attack 来自定义响应,例如返回一个 JSON 响应:
Rack::Attack.throttle("requests by ip", limit: 60, period: 60) do |request|
request.ip
end
Rack::Attack.throttled_response = lambda do |env|
body = { error: "Throttle limit reached. Please try again later." }
[429, { "Content-Type" => "application/json" }, [body.to_json]]
end
上面的代码将限制每个 IP 地址在 1 分钟内最多只能发送 60 个请求。如果超过了这个限制,Rack-Attack 将返回一个 JSON 响应。
高级功能
使用白名单
你可以使用白名单来允许某些请求。例如,你可以允许某个 IP 地址访问你的应用程序:
Rack::Attack.safelist('allow from localhost') do |req|
req.ip == '127.0.0.1'
end
上面的代码将允许来自 127.0.0.1 的请求通过。
自定义限制器
你可以使用自定义的限制器来实现更复杂的限制策略。例如,你可以根据用户的 ID 和 IP 地址来限制请求速率:
class UserThrottle < Rack::Attack::Throttle
def initialize(*)
super
@cache = ActiveSupport::Cache::MemoryStore.new
end
def allowed?(request)
user_id = request.env['warden'].user&.id
return true unless user_id
key = "throttle/user/#{user_id}/#{request.ip}"
count = @cache.fetch(key, expires_in: period) { 0 }
count < limit
end
def throttled_response
[429, {}, ['Too Many Requests']]
end
end
Rack::Attack.throttle('user requests by ip', limit: 5, period: 20.seconds) do |req|
UserThrottle.new(req.ip, req.env).allowed?(req)
end
上面的代码将限制每个用户在 20 秒内最多只能发送 5 个请求,且每个 IP 地址只能发送一个请求。如果超过了这个限制,Rack-Attack 将返回一个 429 Too Many Requests 响应。
使用前置条件
你可以使用前置条件来限制某些请求。例如,你可以限制只有在用户已经登录的情况下才能访问某些页面:
Rack::Attack.preempt('restrict access to /admin') do |req|
req.path == '/admin' && !req.env['warden'].user
end
上面的代码将阻止未登录用户访问 /admin 页面,并返回一个 401 Unauthorized 响应。
使用回调
你可以使用回调来记录被限制的请求。例如,你可以将被限制的请求记录到日志中:
Rack::Attack.throttle('logins/ip', limit: 5, period: 20.seconds) do |req|
if req.path == '/login' && req.post?
req.ip
end
end
Rack::Attack.throttled_response = lambda do |env|
Rails.logger.info "[Rack::Attack] Throttle limit exceeded for #{env['rack.attack.matched']}"
[429, {}, ['Too Many Requests']]
end
上面的代码将记录被限制的登录请求,并将其写入 Rails 的日志文件中。
总结
Rack-Attack 是一个非常有用的 gem,可以帮助你保护你的 Web 应用程序免受恶意攻击。本文档介绍了 Rack-Attack 的基本用法和高级功能,可以帮助你更好地使用 Rack-Attack 来保护你的应用程序。