rack-attack icon indicating copy to clipboard operation
rack-attack copied to clipboard

Throlled/blocklist by response code

Open KieranP opened this issue 11 months ago • 2 comments

All examples and usages show how we can use the request to determine whether to block/throttle. However, is there any way to do so using the response? Using the request IP would mean that if an entire office logged in all around the same time on the same IP, they would throttle themselves, even if all those login requests were successful. Instead, I I want to throttle login requests by IP but only when those requests fail (i.e. a code other than 2xx). That way, when there are 5 or more unsuccessful login attempts within 5 minutes, it'll throttle/block the IP that caused that failed response. Is this possible?

KieranP avatar Dec 02 '24 01:12 KieranP

Hi @KieranP

Using the request IP would mean that if an entire office logged in all around the same time on the same IP, they would throttle themselves, even if all those login requests were successful

That's correct. Maybe another option would be using the email, so different users on the same IP won't block each other. Here is an example https://github.com/rack/rack-attack/blob/main/examples/rack_attack.rb#L15-L20

santib avatar Dec 03 '24 15:12 santib

@santib The trouble though is that doesn't provide any mitigations against simple attackers from scraping credentials or trying to break into accounts. They'll be able to try 1000s of emails without being locked out.

After evaluating rack-attack and several other options, we decided we needed to roll out our own throttling system that could be based on the login response, not just the request. It works something like this:

around_action :throttle_failed_logins, only: :create

def create
  ...
end

private

def throttle_failed_logins
  if throttle_attempts_exceeded?
    head(429) && return
  end

  yield

  if response.status.in?([400, 401, 403, 404])
    increment_failed_attempts!
  end

  if throttle_attempts_exceeded?
    head(429) && return
  end

  true
end

And this seems to be working well. It will only increment per IP when the request fails. That way, if everyone in an office login successfully, no issue. It only triggers when there are 5+ failed logins from the same IP.

Bit of a pain to roll our own code instead of using something like rack-attack, and if rack-attack were to support this use case in the future we'd likely switch over to it, but for now this will do for us.

KieranP avatar Dec 03 '24 20:12 KieranP

Hi @KieranP, sorry for the delay. Seems like blocking based on the response has always worked despite not being documented.

Please refer to https://github.com/rack/rack-attack/pull/646#issuecomment-2861354852 to see how to get it working with rack-attack.

Thank you

santib avatar Sep 02 '25 14:09 santib

@santib I would recommend documenting that, otherwise someone else will open a ticket in the future asking the same thing :-)

KieranP avatar Sep 02 '25 20:09 KieranP