kickstarter/rack-attack — pull request #94 by wkimeria (2014-10-06 22:02:56+00:00)
Title: Create conditional throttles

Re-opened with use case clarification

Hi @ktheory.

I don't think that solution(your comment in the closed pull request )will work for us. In theory, (, we could see whether a certain email address was previously throttled

Rack::Attack.throttles['email'].get_count(....

But the problem is that in our scenario, we still want to conditionally throttle, and there does not seem to be a way around that.

Here is our use case

We have web logins that we want to lock out after a certain number of failed attempts. We might want to configure this to be such that if you enter 5 consecutive invalid passwords within a given time period (i.e an hour) we lock you out of your account (for an hour). In that scenario, we want to increment that counter after each failed login attempt (meaning that we have to process the request). After that, when another request comes in, we reject it outright until the value in redis expires.

The reason the approach above will not work is because what we really want to do is throttle based on a processed request, not just an incoming on (in our case, increment every time login fails).

In theory, this could work if there was a method exposed on the Throttle object that would directly increment. Right now, we can do the following

Rack::Attack.blacklist('bad-logins') { |request|
# Block request if it has been previously throttled
!(Rack::Attack.throttles['email'].get_count(request.params['email']) == 0)
}

We can also create a dummy throttle

Rack::Attack.throttle('email',
:limit =>3,
:period => 1.hour) { |request|
# Do nothing, this is a placeholder just to create the throttle
}

Where this falls down is how do we increment the counter?

# Some Logic in the controller
# Invalid login
Rack::Attack.throttles['email'].increment(request['email'] <-- This method or similar method does not exist