kivanio (owner)

Fork Of

Revisions

gist: 227924 Download_button fork
public
Public Clone URL: git://gist.github.com/227924.git
Embed All Files: show embed
rack_brute_force_killa.rb #
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# BruteForceKilla
#
# A Rack middleware to limit requests by ip address, coded for fun as my first
# middleware, thanks http://coderack.org for giving me a reason :)
#
# For production use, one would want to make a memcache or redis tracker.
#
# options:
#
# :tracker => Class name of the tracker to use (default Memory (all there is for now!))
# :sample_count => # of requests from ip before calcing the duration (default 10)
# :duration => Max allowed duration in seconds (default 1 second)
 
module Rack
  module BruteForceKillaTracker
    # Tracks ip addresses in memory, local to the process. Not recommeded for most
    # apps, provided as a contract example, specifically trackers need to:
    #
    # 1) initialize the :sample_count, :duration options (other opts pass-thru from middleware)
    # 2) respond_to kill?(env) -- you can grab the ip address from env['REMOTE_ADDR']
    class Memory
      
      def initialize(options={})
        @sample_count = options[:sample_count]
        @duration = options[:duration]
      end
      
      def ip_addresses
        @ip_addresses ||= {}
      end
      
      def kill?(env)
        ip_address = ['REMOTE_ADDR']
        hits = (ip_addresses[ip_address] ||= [])
        hits << Time.now.to_i
        if hits.size >= @sample_count
          sum = 0
          for x in 0..(@sample_count-2)
            sum += (hits[x+1] - hits[x])
          end
          ip_addresses.delete(ip_address)
          (sum/@sample_count) < @duration ? true : false
        else
          ip_addresses.clear if ip_addresses.size > 2000
          false
        end
      end
    end
  end
end
 
module Rack
  class BruteForceKilla
 
    def initialize(app,options={})
      @tracker = (options.delete(:tracker) || BruteForceKillaTracker::Memory).new({:sample_count=>10,:duration=>1}.merge!(options))
      @app = app
    end
    
    def call(env)
      if @tracker.kill?(env)
        msg = "Request rate violation"
        [403,{"Content-Length"=>msg.length.to_s},msg]
      else
        @app.call(env)
      end
    end
 
  end
end