rate_limit 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in rate_limit.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2012 Geoffroy Couprie
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
data/README.md ADDED
@@ -0,0 +1,33 @@
1
+ Rate Limit
2
+ ======
3
+
4
+ Filter requests to your controllers, and block bruteforce attacks on your forms.
5
+
6
+ By limiting the rate of requests, you could, without harming usability:
7
+ * prevent password bruteforce on login forms
8
+ * prevent user enumeration on password reset forms
9
+ * slow significantly site scraping
10
+
11
+ Features
12
+ --------
13
+
14
+ * Block requests if they exceed a specified rate (blockBy*)
15
+ * Add a growing delay before accepting requests (slowBy*)
16
+ * Reset the filter if successful use (resetBlockerByKey/resetSlowerByKey)
17
+
18
+ Code example
19
+ ------------
20
+
21
+ ```ruby
22
+ class HelloController < ApplicationController
23
+ def index
24
+ if RateLimit.slowByIp(request)
25
+ puts "not blocking the page"
26
+ else
27
+ puts "blocking the page"
28
+ render :nothing => true, :status => 403
29
+ end
30
+ end
31
+ end
32
+ ```
33
+
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/lib/rate_limit.rb ADDED
@@ -0,0 +1,84 @@
1
+ require "rate_limit/version"
2
+ require "rate_limit/loader"
3
+ require "rate_limit/visit"
4
+
5
+ module RateLimit
6
+
7
+ ##
8
+ # Block requests by IP
9
+ # the request argument is given by the request method of Rails controllers
10
+
11
+ def self.limitByIp(request, requests_per_seconds = 1, cache = Rails.cache)
12
+ key = request.remote_ip+request.fullpath
13
+ blockByKey(key, requests_per_seconds, cache)
14
+ end
15
+
16
+ ##
17
+ # Slow requests by IP
18
+ # the factor parameter is the multiplicating factor for the growing time between requests
19
+ def self.slowByIp(request, requests_per_seconds = 1, factor = 1, cache = Rails.cache)
20
+ key = request.remote_ip+request.fullpath
21
+ slowByKey(key, requests_per_seconds, factor, cache)
22
+ end
23
+
24
+ ##
25
+ # Block requests by key
26
+ # You can define wich eky is used to discriminate between requests
27
+ def self.blockByKey(key, requests_per_seconds = 1, cache = Rails.cache)
28
+ loader = Loader.new cache
29
+
30
+ key = key+"b"
31
+ timestamp = Time.new.to_time.to_i
32
+ counter = loader.read key
33
+ if counter.nil?
34
+ loader.write key, BlockedVisit.new(1, timestamp)
35
+ true
36
+ else
37
+ counter.visits += 1
38
+ time = timestamp - counter.start_time
39
+ loader.write key, counter
40
+ requests_per_seconds > counter.visits.to_f/time.to_f
41
+ end
42
+ end
43
+
44
+ ##
45
+ # Slow requests by key
46
+ # You can define wich eky is used to discriminate between requests
47
+ def self.slowByKey(key, requests_per_seconds = 0.5, factor = 1, cache = Rails.cache)
48
+ loader = Loader.new cache
49
+
50
+ key = key+"s"
51
+ timestamp = Time.new.to_time.to_i
52
+ counter = loader.read key
53
+ if counter.nil?
54
+ loader.write key, SlowedVisit.new(1, timestamp)
55
+ true
56
+ else
57
+ time = timestamp - counter.previous
58
+ counter.previous = timestamp
59
+ wait_time = requests_per_seconds*factor*counter.visits
60
+ if wait_time < time
61
+ loader.write key, counter
62
+ true
63
+ else
64
+ counter.visits += 1
65
+ loader.write key, counter
66
+ false
67
+ end
68
+ end
69
+ end
70
+
71
+ ##
72
+ # Reset the counters for blocked requests
73
+ def self.resetBlockerByKey(key)
74
+ loader = Loader.new cache
75
+ cache.delete(key+"b")
76
+ end
77
+
78
+ ##
79
+ # Reset the counters for slowed requests
80
+ def self.resetSlowerByKey(key)
81
+ loader = Loader.new cache
82
+ cache.delete(key+"s")
83
+ end
84
+ end
@@ -0,0 +1,18 @@
1
+ class RateLimit::Loader
2
+ def initialize(cache)
3
+ @cache = cache
4
+ end
5
+
6
+ def read(key)
7
+ data = @cache.read(key)
8
+ Marshal::load(data) unless data.nil?
9
+ end
10
+
11
+ def write(key, data)
12
+ @cache.write(key, Marshal::dump(data))
13
+ end
14
+
15
+ def delete(key)
16
+ @cache.delete(key)
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ module RateLimit
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,15 @@
1
+ class RateLimit::BlockedVisit
2
+ attr_accessor :visits, :start_time
3
+ def initialize(visits, date)
4
+ @visits = visits
5
+ @start_time = date
6
+ end
7
+ end
8
+
9
+ class RateLimit::SlowedVisit
10
+ attr_accessor :visits, :previous
11
+ def initialize(visits = 0, previous)
12
+ @visits = visits
13
+ @previous = previous
14
+ end
15
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "rate_limit/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "rate_limit"
7
+ s.version = RateLimit::VERSION
8
+ s.authors = ["Geoffroy Couprie"]
9
+ s.email = ["geo.couprie@gmail.com"]
10
+ s.homepage = "https://github.com/Geal/rate_limit"
11
+ s.summary = %q{rate_limit filters page access in your Rails application}
12
+ s.description = %q{Limit request frequence by IP, by page, for login or password reset forms}
13
+
14
+ s.rubyforge_project = "rate_limit"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ # s.add_development_dependency "rspec"
23
+ # s.add_runtime_dependency "rest-client"
24
+ end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rate_limit
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Geoffroy Couprie
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2012-07-12 00:00:00 Z
14
+ dependencies: []
15
+
16
+ description: Limit request frequence by IP, by page, for login or password reset forms
17
+ email:
18
+ - geo.couprie@gmail.com
19
+ executables: []
20
+
21
+ extensions: []
22
+
23
+ extra_rdoc_files: []
24
+
25
+ files:
26
+ - .gitignore
27
+ - Gemfile
28
+ - LICENSE
29
+ - README.md
30
+ - Rakefile
31
+ - lib/rate_limit.rb
32
+ - lib/rate_limit/loader.rb
33
+ - lib/rate_limit/version.rb
34
+ - lib/rate_limit/visit.rb
35
+ - rate_limit.gemspec
36
+ homepage: https://github.com/Geal/rate_limit
37
+ licenses: []
38
+
39
+ post_install_message:
40
+ rdoc_options: []
41
+
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ requirements: []
57
+
58
+ rubyforge_project: rate_limit
59
+ rubygems_version: 1.8.8
60
+ signing_key:
61
+ specification_version: 3
62
+ summary: rate_limit filters page access in your Rails application
63
+ test_files: []
64
+