Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Throttling Basic Auth #47

Closed
jonathan-mui opened this issue Mar 9, 2014 · 3 comments
Closed

Throttling Basic Auth #47

jonathan-mui opened this issue Mar 9, 2014 · 3 comments

Comments

@jonathan-mui
Copy link

I'm wondering if there's a way for me to throttle basic auth requests to protect against brute force attacks.
I posted on SO, but haven't had an response.
http://stackoverflow.com/questions/22261081/using-rackattack-to-throttle-basic-auth

My basic auth code setup like so:

# config/environments/production.rb
config.middleware.insert_after(::Rack::Lock, '::Rack::Auth::Basic', ENV['RAILS_ENV') do |u, p|
  [u, p] == [ENV['BASIC_AUTH_USERNAME'], ENV['BASIC_AUTH_PASSWORD']]
end

I'd imagine my rack attack would contain something like this:

# config/initializers/rack_attack.rb
Rack::Attack.throttle('req/ip', limit: 2, period: 60.seconds) do |req| req.ip 
    # what should belong here???
end
@ktheory
Copy link
Collaborator

ktheory commented Mar 9, 2014

Good question! I think the simplest approach would be to use the Allow2Ban feature.

# After 5 requests with incorrect auth in 1 minute,
# block all requests from that IP for 1 hour.
Rack::Attack.blacklist('basic auth crackers') do |req|
  Rack::Attack::Allow2Ban.filter(req.ip, :maxretry => 5, :findtime => 1.minute, :bantime => 1.hour) do
    # Return true if the authorization header not incorrect
    auth = Rack::Auth::Basic::Request.new(req.ev)
    auth.credentials != [my_username, my_password]
  end
end

So legit users can fiddle with their password a few times; but too many times and any req from that IP will be blocked.

The only thing I don't like about this approach is that checking the username/password happens twice: once n Rack::Attack to know if the req should count towards the IP's limit; and once in your app code to check that the user is authorized. Can't think of a way to get around that without making everything more complicated.

@ktheory ktheory closed this as completed Mar 9, 2014
@jonathan-mui
Copy link
Author

Awesome! Thank you!

@toshe
Copy link

toshe commented Mar 1, 2015

The only thing I don't like about this approach is that checking the username/password happens twice: once n Rack::Attack to know if the req should count towards the IP's limit; and once in your app code to check that the user is authorized. Can't think of a way to get around that without making everything more complicated.

Actually there is a simple way to do this. If every request passes through rack-attack then you're practically already performing the authorization there. What you can do is is modify the req.env and add a TrueClass indicating if the request has already been authorized. This way rack-attack can act as an Authorization Middleware as well (just make sure that it's the first in the stack)

Rack::Attack.blacklist('basic auth crackers') do |req|
  Rack::Attack::Allow2Ban.filter(req.ip, :maxretry => 5, :findtime => 1.minute, :bantime => 1.hour) do
    # Return true if the authorization header not incorrect
    ban_user = false
    auth = Rack::Auth::Basic::Request.new(req.ev)
    if auth.credentials != [my_username, my_password]
        req.env['authorized'] = false
        ban_user = true
    else
        req.env['authorized'] = true
        req.env['user_name] = my_username
        ban_user = false
    end
    ban_user
  end
end

⚠️ code is untested, beware! 🪲

This way you can simply access request.env['authorized'] in your middleware (Rails, Sinatra) and perform further authentication. You could also theoretically do the same for sessions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants