Rack::Attack: Protect your app with this one weird gem!

Rack::Attack: Protect your app with this one weird gem!

Mature apps face problems with abusive requests like misbehaving users, malicious hackers, and naive scrapers. Too often they drain developer productivity and happiness.

Rack::Attack is middleware to easily throttle abusive requests.

At Kickstarter, we built it to keep our site fast and reliable with little effort. Learn how Rack::Attack works through examples from kickstarter.com. Spend less time dealing with bad apples, and more time on the fun stuff.

5888fc25101419e40b7de521f8524dad?s=128

Aaron Suggs

April 22, 2014
Tweet

Transcript

  1. Rack::Attack Protect your app with this one weird gem!

  2. is a funding platform for creative projects

  3. Aaron Suggs ktheory Ops Engineer KICKSTARTER

  4. Rack::Attack Middleware for blocking & throttling

  5. /kickstarter/rack-attack

  6. > performance

  7. > availability

  8. > happiness

  9. The origin story

  10. None
  11. I want to stop bad requests.

  12. I want rack middleware.

  13. # Example rack middleware class MyMiddleware def initialize(app) @app =

    app end ! def call(env) @app.call(env) end end
  14. # Example rack middleware class MyMiddleware def initialize(app) @app =

    app end ! def call(env) # Do stuff with the request @app.call(env) end end
  15. # Example rack middleware class MyMiddleware def initialize(app) @app =

    app end ! def call(env) # Do stuff with the request @app.call(env) # Do stuff with the response end end
  16. # Rack::Attack is basically: def call(env) if should_allow?(env) @app.call(env) else

    access_denied end end
  17. # In an initializer… # Throttle IPs to 10 reqs

    every 5 seconds module Rack::Attack throttle("ip", limit: 10, period: 5) {|req| req.ip } end
  18. # In an initializer… # Throttle IPs to 10 reqs

    every 5 seconds module Rack::Attack throttle("ip", limit: 10, period: 5) {|req| req.ip } end Rack::Request.new(env)
  19. # In an initializer… # Throttle IPs to 10 reqs

    every 5 seconds module Rack::Attack throttle("ip", limit: 10, period: 5) {|req| req.ip } end Discriminator
  20. Throttle state •Rails.cache by default •memcached or redis •build your

    own
  21. # Making the cache key now = Time.now.to_i ! #

    Key changes each period key = "#{name}:#{now/period}:#{block_return}" ! # => "ip:279569154:127.0.0.1"
  22. # Key expires at the end of this period expires_in

    = period - (now % period)
  23. # Middleware increments counter and blocks count = @cache.increment(key, expires_in)

    ! access_denied if count > limit
  24. The sequel

  25. kicksniper.py

  26. None
  27. Throttled

  28. Pro tips

  29. # Throttle logins per IP throttle('logins/ip', limit: 5, period: 20)

    {|req| ! if req.path == '/login' && req.post? req.ip end ! }
  30. # Throttle logins per email throttle('logins/email', limit: 5, period: 20)

    {|req| ! if req.path == '/login' && req.post? req.params['email'].presence end ! }
  31. blacklists Not even once.

  32. # Easy VPN OFFICE_IP = '1.2.3.4' ! blacklist('bad_admin_ip') {|req| req.path.start_with?('/admin')

    && req.ip != OFFICE_IP }
  33. # Starve the trolls # <3 our community support team

    BANNED_IPS = Set.new ['1.2.3.4', '5.6.7.8'] ! blacklist('banned_ip') {|req| BANNED_IPS.include?(req.ip) && ! req.get? }
  34. ActiveSupport::Notifications for metrics

  35. Rack::Attack compliments •iptables •nginx limit_conn_zone •CDN, Web App Firewall

  36. Rack::Attack is •ruby •easy to test •easy to deploy

  37. Thank you, contributors

  38. Julian Doherty @ barelyknown Vipul A M Hakan Ensari Zach

    Millman @ alexchee Steve Hodgkiss T.J. Schuck Carsten Zimmermann Salimane Adjao Moustapha Jordan Moncharmont Pedro Nascimento Han Richard Schneeman Tieg Zaharia Navin Tristan Dunn Michael Jelks
  39. The web stays weird.

  40. The web site stays up.

  41. /kickstarter/rack-attack ktheory Image CC BY flickr.com/matthamm