Add support for rate limiting signup requests
This commit is contained in:
parent
7054cea48e
commit
63bf18a3c3
4 changed files with 82 additions and 1 deletions
|
@ -186,6 +186,9 @@ class UsersController < ApplicationController
|
|||
end
|
||||
|
||||
if current_user.save
|
||||
SIGNUP_IP_LIMITER&.update(request.remote_ip)
|
||||
SIGNUP_EMAIL_LIMITER&.update(canonical_email(current_user.email))
|
||||
|
||||
flash[:matomo_goal] = Settings.matomo["goals"]["signup"] if defined?(Settings.matomo)
|
||||
|
||||
referer = welcome_path
|
||||
|
@ -344,7 +347,13 @@ class UsersController < ApplicationController
|
|||
domain_mx_servers(domain)
|
||||
end
|
||||
|
||||
if blocked = Acl.no_account_creation(request.remote_ip, :domain => domain, :mx => mx_servers)
|
||||
blocked = Acl.no_account_creation(request.remote_ip, :domain => domain, :mx => mx_servers)
|
||||
|
||||
blocked ||= SIGNUP_IP_LIMITER && !SIGNUP_IP_LIMITER.allow?(request.remote_ip)
|
||||
|
||||
blocked ||= email && SIGNUP_EMAIL_LIMITER && !SIGNUP_EMAIL_LIMITER.allow?(canonical_email(email))
|
||||
|
||||
if blocked
|
||||
logger.info "Blocked signup from #{request.remote_ip} for #{email}"
|
||||
|
||||
render :action => "blocked"
|
||||
|
@ -353,6 +362,20 @@ class UsersController < ApplicationController
|
|||
!blocked
|
||||
end
|
||||
|
||||
def canonical_email(email)
|
||||
local_part, domain = if email.nil?
|
||||
nil
|
||||
else
|
||||
email.split("@")
|
||||
end
|
||||
|
||||
local_part.sub!(/\+.*$/, "")
|
||||
|
||||
local_part.delete!(".") if %w[gmail.com googlemail.com].include?(domain)
|
||||
|
||||
"#{local_part}@#{domain}"
|
||||
end
|
||||
|
||||
##
|
||||
# get list of MX servers for a domains
|
||||
def domain_mx_servers(domain)
|
||||
|
|
15
config/initializers/rate_limits.rb
Normal file
15
config/initializers/rate_limits.rb
Normal file
|
@ -0,0 +1,15 @@
|
|||
require "rate_limiter"
|
||||
|
||||
SIGNUP_IP_LIMITER = if Settings.memcache_servers && Settings.signup_ip_per_day && Settings.signup_ip_max_burst
|
||||
RateLimiter.new(
|
||||
Dalli::Client.new(Settings.memcache_servers, :namespace => "rails:signup:ip"),
|
||||
86400, Settings.signup_ip_per_day, Settings.signup_ip_max_burst
|
||||
)
|
||||
end
|
||||
|
||||
SIGNUP_EMAIL_LIMITER = if Settings.memcache_servers && Settings.signup_email_per_day && Settings.signup_email_max_burst
|
||||
RateLimiter.new(
|
||||
Dalli::Client.new(Settings.memcache_servers, :namespace => "rails:signup:email"),
|
||||
86400, Settings.signup_email_per_day, Settings.signup_email_max_burst
|
||||
)
|
||||
end
|
|
@ -140,3 +140,8 @@ smtp_user_name: null
|
|||
smtp_password: null
|
||||
# Matomo settings for analytics
|
||||
#matomo:
|
||||
# Signup rate limits
|
||||
#signup_ip_per_day:
|
||||
#signup_ip_max_burst:
|
||||
#signup_email_per_day:
|
||||
#signup_email_max_burst:
|
||||
|
|
38
lib/rate_limiter.rb
Normal file
38
lib/rate_limiter.rb
Normal file
|
@ -0,0 +1,38 @@
|
|||
class RateLimiter
|
||||
def initialize(cache, interval, limit, max_burst)
|
||||
@cache = cache
|
||||
@requests_per_second = limit.to_f / interval
|
||||
@burst_limit = max_burst
|
||||
end
|
||||
|
||||
def allow?(key)
|
||||
last_update, requests = @cache.get(key)
|
||||
|
||||
if last_update
|
||||
elapsed = Time.now.to_i - last_update
|
||||
|
||||
requests -= elapsed * @requests_per_second
|
||||
else
|
||||
requests = 0.0
|
||||
end
|
||||
|
||||
requests < @burst_limit
|
||||
end
|
||||
|
||||
def update(key)
|
||||
now = Time.now.to_i
|
||||
|
||||
last_update, requests = @cache.get(key)
|
||||
|
||||
if last_update
|
||||
elapsed = now - last_update
|
||||
|
||||
requests -= elapsed * @requests_per_second
|
||||
requests += 1.0
|
||||
else
|
||||
requests = 1.0
|
||||
end
|
||||
|
||||
@cache.set(key, [now, [requests, 1.0].max])
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue