rack-protection 1.5.0 → 1.5.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.
Potentially problematic release.
This version of rack-protection might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/README.md +8 -0
- data/lib/rack/protection/authenticity_token.rb +10 -3
- data/lib/rack/protection/base.rb +8 -1
- data/lib/rack/protection/json_csrf.rb +2 -2
- data/lib/rack/protection/path_traversal.rb +16 -5
- data/lib/rack/protection/version.rb +1 -1
- data/rack-protection.gemspec +25 -11
- data/spec/authenticity_token_spec.rb +15 -0
- data/spec/base_spec.rb +9 -0
- data/spec/json_csrf_spec.rb +14 -0
- data/spec/path_traversal_spec.rb +15 -2
- data/spec/protection_spec.rb +21 -0
- metadata +34 -28
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d142ba2e517a486c07d79a7c7a80061fe405a84d
|
4
|
+
data.tar.gz: 43a1d8c17e3bc26171c5c75bc22eaded63b4587c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: df0552ef6da37611e34ff91e664521794681596cfd3898bf1cd5acb8150eea553db1bc761dc9fc3c90900884a3f5128368096bd9a1e386c84e90fe9c0fb83431
|
7
|
+
data.tar.gz: 4903af66fc37585786fb5650f9475695d3f0455f984c2621bc35ed9ae7addb19588fbc55d134cbf891f972c0316305e426c0d90ecdd9431273ad69a37585ef92
|
data/README.md
CHANGED
@@ -80,3 +80,11 @@ Prevented by:
|
|
80
80
|
|
81
81
|
gem install rack-protection
|
82
82
|
|
83
|
+
# Instrumentation
|
84
|
+
|
85
|
+
Instrumentation is enabled by passing in an instrumenter as an option.
|
86
|
+
```
|
87
|
+
use Rack::Protection, instrumenter: ActiveSupport::Notifications
|
88
|
+
```
|
89
|
+
|
90
|
+
The instrumenter is passed a namespace (String) and environment (Hash). The namespace is 'rack.protection' and the attack type can be obtained from the environment key 'rack.protection.attack'.
|
@@ -11,13 +11,20 @@ module Rack
|
|
11
11
|
# included in the session.
|
12
12
|
#
|
13
13
|
# Compatible with Rails and rack-csrf.
|
14
|
+
#
|
15
|
+
# Options:
|
16
|
+
#
|
17
|
+
# authenticity_param: Defines the param's name that should contain the token on a request.
|
18
|
+
#
|
14
19
|
class AuthenticityToken < Base
|
20
|
+
default_options :authenticity_param => 'authenticity_token'
|
21
|
+
|
15
22
|
def accepts?(env)
|
16
|
-
return true if safe? env
|
17
23
|
session = session env
|
18
24
|
token = session[:csrf] ||= session['_csrf_token'] || random_string
|
19
|
-
env
|
20
|
-
|
25
|
+
safe?(env) ||
|
26
|
+
env['HTTP_X_CSRF_TOKEN'] == token ||
|
27
|
+
Request.new(env).params[options[:authenticity_param]] == token
|
21
28
|
end
|
22
29
|
end
|
23
30
|
end
|
data/lib/rack/protection/base.rb
CHANGED
@@ -44,6 +44,7 @@ module Rack
|
|
44
44
|
def call(env)
|
45
45
|
unless accepts? env
|
46
46
|
warn env, "attack prevented by #{self.class}"
|
47
|
+
instrument env
|
47
48
|
result = react env
|
48
49
|
end
|
49
50
|
result or app.call(env)
|
@@ -60,6 +61,12 @@ module Rack
|
|
60
61
|
l.warn(message)
|
61
62
|
end
|
62
63
|
|
64
|
+
def instrument(env)
|
65
|
+
return unless i = options[:instrumenter]
|
66
|
+
env['rack.protection.attack'] = self.class.name.split('::').last.downcase
|
67
|
+
i.instrument('rack.protection', env)
|
68
|
+
end
|
69
|
+
|
63
70
|
def deny(env)
|
64
71
|
[options[:status], {'Content-Type' => 'text/plain'}, [options[:message]]]
|
65
72
|
end
|
@@ -92,7 +99,7 @@ module Rack
|
|
92
99
|
end
|
93
100
|
|
94
101
|
def random_string(secure = defined? SecureRandom)
|
95
|
-
secure ? SecureRandom.hex(
|
102
|
+
secure ? SecureRandom.hex(16) : "%032x" % rand(2**128-1)
|
96
103
|
rescue NotImplementedError
|
97
104
|
random_string false
|
98
105
|
end
|
@@ -11,7 +11,7 @@ module Rack
|
|
11
11
|
# Array prototype has been patched to track data. Checks the referrer
|
12
12
|
# even on GET requests if the content type is JSON.
|
13
13
|
class JsonCsrf < Base
|
14
|
-
|
14
|
+
alias react deny
|
15
15
|
|
16
16
|
def call(env)
|
17
17
|
request = Request.new(env)
|
@@ -19,7 +19,7 @@ module Rack
|
|
19
19
|
|
20
20
|
if has_vector? request, headers
|
21
21
|
warn env, "attack prevented by #{self.class}"
|
22
|
-
react(env)
|
22
|
+
react(env) or [status, headers, body]
|
23
23
|
else
|
24
24
|
[status, headers, body]
|
25
25
|
end
|
@@ -19,16 +19,27 @@ module Rack
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def cleanup(path)
|
22
|
+
if path.respond_to?(:encoding)
|
23
|
+
# Ruby 1.9+ M17N
|
24
|
+
encoding = path.encoding
|
25
|
+
dot = '.'.encode(encoding)
|
26
|
+
slash = '/'.encode(encoding)
|
27
|
+
else
|
28
|
+
# Ruby 1.8
|
29
|
+
dot = '.'
|
30
|
+
slash = '/'
|
31
|
+
end
|
32
|
+
|
22
33
|
parts = []
|
23
|
-
unescaped = path.gsub(
|
34
|
+
unescaped = path.gsub(/%2e/i, dot).gsub(/%2f/i, slash)
|
24
35
|
|
25
|
-
unescaped.split(
|
26
|
-
next if part.empty? or part ==
|
36
|
+
unescaped.split(slash).each do |part|
|
37
|
+
next if part.empty? or part == dot
|
27
38
|
part == '..' ? parts.pop : parts << part
|
28
39
|
end
|
29
40
|
|
30
|
-
cleaned =
|
31
|
-
cleaned <<
|
41
|
+
cleaned = slash + parts.join(slash)
|
42
|
+
cleaned << slash if parts.any? and unescaped =~ %r{/\.{0,2}$}
|
32
43
|
cleaned
|
33
44
|
end
|
34
45
|
end
|
data/rack-protection.gemspec
CHANGED
@@ -2,18 +2,20 @@
|
|
2
2
|
Gem::Specification.new do |s|
|
3
3
|
# general infos
|
4
4
|
s.name = "rack-protection"
|
5
|
-
s.version = "1.5.
|
5
|
+
s.version = "1.5.1"
|
6
6
|
s.description = "You should use protection!"
|
7
7
|
s.homepage = "http://github.com/rkh/rack-protection"
|
8
8
|
s.summary = s.description
|
9
|
+
s.license = 'MIT'
|
9
10
|
|
10
11
|
# generated from git shortlog -sn
|
11
12
|
s.authors = [
|
12
13
|
"Konstantin Haase",
|
13
14
|
"Alex Rodionov",
|
14
|
-
"
|
15
|
-
"
|
16
|
-
"
|
15
|
+
"Patrick Ellis",
|
16
|
+
"Jeff Welling",
|
17
|
+
"ITO Nobuaki",
|
18
|
+
"Matteo Centenaro",
|
17
19
|
"David Kellum",
|
18
20
|
"Egor Homakov",
|
19
21
|
"Florian Gilcher",
|
@@ -23,18 +25,24 @@ Gem::Specification.new do |s|
|
|
23
25
|
"SAKAI, Kazuaki",
|
24
26
|
"Stanislav Savulchik",
|
25
27
|
"Steve Agalloco",
|
26
|
-
"Akzhan Abdulin",
|
27
28
|
"TOBY",
|
28
|
-
"
|
29
|
+
"Akzhan Abdulin",
|
30
|
+
"brookemckim",
|
31
|
+
"Bj\u00F8rge N\u00E6ss",
|
32
|
+
"Chris Heald",
|
33
|
+
"Chris Mytton",
|
34
|
+
"Corey Ward",
|
35
|
+
"Dario Cravero"
|
29
36
|
]
|
30
37
|
|
31
38
|
# generated from git shortlog -sne
|
32
39
|
s.email = [
|
33
40
|
"konstantin.mailinglists@googlemail.com",
|
34
41
|
"p0deje@gmail.com",
|
35
|
-
"
|
36
|
-
"
|
37
|
-
"
|
42
|
+
"patrick@soundcloud.com",
|
43
|
+
"jeff.welling@gmail.com",
|
44
|
+
"bugant@gmail.com",
|
45
|
+
"daydream.trippers@gmail.com",
|
38
46
|
"homakov@gmail.com",
|
39
47
|
"florian.gilcher@asquera.de",
|
40
48
|
"developer@fojasaur.us",
|
@@ -43,10 +51,15 @@ Gem::Specification.new do |s|
|
|
43
51
|
"kaz.july.7@gmail.com",
|
44
52
|
"s.savulchik@gmail.com",
|
45
53
|
"steve.agalloco@gmail.com",
|
46
|
-
"akzhan.abdulin@gmail.com",
|
47
54
|
"toby.net.info.mail+git@gmail.com",
|
55
|
+
"akzhan.abdulin@gmail.com",
|
56
|
+
"brooke@digitalocean.com",
|
48
57
|
"bjoerge@bengler.no",
|
49
|
-
"cheald@gmail.com"
|
58
|
+
"cheald@gmail.com",
|
59
|
+
"self@hecticjeff.net",
|
60
|
+
"coreyward@me.com",
|
61
|
+
"dario@uxtemple.com",
|
62
|
+
"dek-oss@gravitext.com"
|
50
63
|
]
|
51
64
|
|
52
65
|
# generated from git ls-files
|
@@ -72,6 +85,7 @@ Gem::Specification.new do |s|
|
|
72
85
|
"lib/rack/protection/xss_header.rb",
|
73
86
|
"rack-protection.gemspec",
|
74
87
|
"spec/authenticity_token_spec.rb",
|
88
|
+
"spec/base_spec.rb",
|
75
89
|
"spec/escaped_params_spec.rb",
|
76
90
|
"spec/form_token_spec.rb",
|
77
91
|
"spec/frame_options_spec.rb",
|
@@ -30,4 +30,19 @@ describe Rack::Protection::AuthenticityToken do
|
|
30
30
|
it "prevents ajax requests without a valid token" do
|
31
31
|
post('/', {}, "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest").should_not be_ok
|
32
32
|
end
|
33
|
+
|
34
|
+
it "allows for a custom authenticity token param" do
|
35
|
+
mock_app do
|
36
|
+
use Rack::Protection::AuthenticityToken, :authenticity_param => 'csrf_param'
|
37
|
+
run proc { |e| [200, {'Content-Type' => 'text/plain'}, ['hi']] }
|
38
|
+
end
|
39
|
+
|
40
|
+
post('/', {"csrf_param" => "a"}, 'rack.session' => {:csrf => "a"})
|
41
|
+
last_response.should be_ok
|
42
|
+
end
|
43
|
+
|
44
|
+
it "sets a new csrf token for the session in env, even after a 'safe' request" do
|
45
|
+
get('/', {}, {})
|
46
|
+
env['rack.session'][:csrf].should_not be_nil
|
47
|
+
end
|
33
48
|
end
|
data/spec/base_spec.rb
ADDED
data/spec/json_csrf_spec.rb
CHANGED
@@ -31,6 +31,7 @@ describe Rack::Protection::JsonCsrf do
|
|
31
31
|
it "accepts XHR requests" do
|
32
32
|
get('/', {}, 'HTTP_REFERER' => 'http://evil.com', 'HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest').should be_ok
|
33
33
|
end
|
34
|
+
|
34
35
|
end
|
35
36
|
|
36
37
|
describe 'not json response' do
|
@@ -41,4 +42,17 @@ describe Rack::Protection::JsonCsrf do
|
|
41
42
|
end
|
42
43
|
|
43
44
|
end
|
45
|
+
|
46
|
+
describe 'with drop_session as default reaction' do
|
47
|
+
it 'still denies' do
|
48
|
+
mock_app do
|
49
|
+
use Rack::Protection, :reaction => :drop_session
|
50
|
+
run proc { |e| [200, {'Content-Type' => 'application/json'}, []]}
|
51
|
+
end
|
52
|
+
|
53
|
+
session = {:foo => :bar}
|
54
|
+
get('/', {}, 'HTTP_REFERER' => 'http://evil.com', 'rack.session' => session)
|
55
|
+
last_response.should_not be_ok
|
56
|
+
end
|
57
|
+
end
|
44
58
|
end
|
data/spec/path_traversal_spec.rb
CHANGED
@@ -14,8 +14,8 @@ describe Rack::Protection::PathTraversal do
|
|
14
14
|
|
15
15
|
{ # yes, this is ugly, feel free to change that
|
16
16
|
'/..' => '/', '/a/../b' => '/b', '/a/../b/' => '/b/', '/a/.' => '/a/',
|
17
|
-
'/%2e.' => '/', '/a/%
|
18
|
-
'//' => '/', '/%2fetc%
|
17
|
+
'/%2e.' => '/', '/a/%2E%2e/b' => '/b', '/a%2f%2E%2e%2Fb/' => '/b/',
|
18
|
+
'//' => '/', '/%2fetc%2Fpasswd' => '/etc/passwd'
|
19
19
|
}.each do |a, b|
|
20
20
|
it("replaces #{a.inspect} with #{b.inspect}") { get(a).body.should == b }
|
21
21
|
end
|
@@ -25,4 +25,17 @@ describe Rack::Protection::PathTraversal do
|
|
25
25
|
app.call({}).should be == 42
|
26
26
|
end
|
27
27
|
end
|
28
|
+
|
29
|
+
if "".respond_to?(:encoding) # Ruby 1.9+ M17N
|
30
|
+
context "PATH_INFO's encoding" do
|
31
|
+
before do
|
32
|
+
@app = Rack::Protection::PathTraversal.new(proc { |e| [200, {'Content-Type' => 'text/plain'}, [e['PATH_INFO'].encoding.to_s]] })
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should remain unchanged as ASCII-8BIT' do
|
36
|
+
body = @app.call({ 'PATH_INFO' => '/'.encode('ASCII-8BIT') })[2][0]
|
37
|
+
body.should == 'ASCII-8BIT'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
28
41
|
end
|
data/spec/protection_spec.rb
CHANGED
@@ -46,4 +46,25 @@ describe Rack::Protection do
|
|
46
46
|
it { should be_false }
|
47
47
|
end
|
48
48
|
end
|
49
|
+
|
50
|
+
describe "#instrument" do
|
51
|
+
let(:env) { { 'rack.protection.attack' => 'base' } }
|
52
|
+
let(:instrumenter) { double('Instrumenter') }
|
53
|
+
|
54
|
+
after do
|
55
|
+
app.instrument(env)
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'with an instrumenter specified' do
|
59
|
+
let(:app) { Rack::Protection::Base.new(nil, :instrumenter => instrumenter) }
|
60
|
+
|
61
|
+
it { instrumenter.should_receive(:instrument).with('rack.protection', env) }
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'with no instrumenter specified' do
|
65
|
+
let(:app) { Rack::Protection::Base.new(nil) }
|
66
|
+
|
67
|
+
it { instrumenter.should_not_receive(:instrument) }
|
68
|
+
end
|
69
|
+
end
|
49
70
|
end
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-protection
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.5.
|
5
|
-
prerelease:
|
4
|
+
version: 1.5.1
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Konstantin Haase
|
9
8
|
- Alex Rodionov
|
10
|
-
-
|
11
|
-
-
|
12
|
-
-
|
9
|
+
- Patrick Ellis
|
10
|
+
- Jeff Welling
|
11
|
+
- ITO Nobuaki
|
12
|
+
- Matteo Centenaro
|
13
13
|
- David Kellum
|
14
14
|
- Egor Homakov
|
15
15
|
- Florian Gilcher
|
@@ -19,50 +19,50 @@ authors:
|
|
19
19
|
- SAKAI, Kazuaki
|
20
20
|
- Stanislav Savulchik
|
21
21
|
- Steve Agalloco
|
22
|
-
- Akzhan Abdulin
|
23
22
|
- TOBY
|
23
|
+
- Akzhan Abdulin
|
24
|
+
- brookemckim
|
24
25
|
- Bjørge Næss
|
26
|
+
- Chris Heald
|
27
|
+
- Chris Mytton
|
28
|
+
- Corey Ward
|
29
|
+
- Dario Cravero
|
25
30
|
autorequire:
|
26
31
|
bindir: bin
|
27
32
|
cert_chain: []
|
28
|
-
date: 2013-
|
33
|
+
date: 2013-10-21 00:00:00.000000000 Z
|
29
34
|
dependencies:
|
30
35
|
- !ruby/object:Gem::Dependency
|
31
36
|
name: rack
|
32
37
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
38
|
requirements:
|
35
|
-
- -
|
39
|
+
- - '>='
|
36
40
|
- !ruby/object:Gem::Version
|
37
41
|
version: '0'
|
38
42
|
type: :runtime
|
39
43
|
prerelease: false
|
40
44
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
45
|
requirements:
|
43
|
-
- -
|
46
|
+
- - '>='
|
44
47
|
- !ruby/object:Gem::Version
|
45
48
|
version: '0'
|
46
49
|
- !ruby/object:Gem::Dependency
|
47
50
|
name: rack-test
|
48
51
|
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
52
|
requirements:
|
51
|
-
- -
|
53
|
+
- - '>='
|
52
54
|
- !ruby/object:Gem::Version
|
53
55
|
version: '0'
|
54
56
|
type: :development
|
55
57
|
prerelease: false
|
56
58
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
59
|
requirements:
|
59
|
-
- -
|
60
|
+
- - '>='
|
60
61
|
- !ruby/object:Gem::Version
|
61
62
|
version: '0'
|
62
63
|
- !ruby/object:Gem::Dependency
|
63
64
|
name: rspec
|
64
65
|
requirement: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
66
|
requirements:
|
67
67
|
- - ~>
|
68
68
|
- !ruby/object:Gem::Version
|
@@ -70,7 +70,6 @@ dependencies:
|
|
70
70
|
type: :development
|
71
71
|
prerelease: false
|
72
72
|
version_requirements: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
73
|
requirements:
|
75
74
|
- - ~>
|
76
75
|
- !ruby/object:Gem::Version
|
@@ -79,9 +78,10 @@ description: You should use protection!
|
|
79
78
|
email:
|
80
79
|
- konstantin.mailinglists@googlemail.com
|
81
80
|
- p0deje@gmail.com
|
82
|
-
-
|
83
|
-
-
|
84
|
-
-
|
81
|
+
- patrick@soundcloud.com
|
82
|
+
- jeff.welling@gmail.com
|
83
|
+
- bugant@gmail.com
|
84
|
+
- daydream.trippers@gmail.com
|
85
85
|
- homakov@gmail.com
|
86
86
|
- florian.gilcher@asquera.de
|
87
87
|
- developer@fojasaur.us
|
@@ -90,10 +90,15 @@ email:
|
|
90
90
|
- kaz.july.7@gmail.com
|
91
91
|
- s.savulchik@gmail.com
|
92
92
|
- steve.agalloco@gmail.com
|
93
|
-
- akzhan.abdulin@gmail.com
|
94
93
|
- toby.net.info.mail+git@gmail.com
|
94
|
+
- akzhan.abdulin@gmail.com
|
95
|
+
- brooke@digitalocean.com
|
95
96
|
- bjoerge@bengler.no
|
96
97
|
- cheald@gmail.com
|
98
|
+
- self@hecticjeff.net
|
99
|
+
- coreyward@me.com
|
100
|
+
- dario@uxtemple.com
|
101
|
+
- dek-oss@gravitext.com
|
97
102
|
executables: []
|
98
103
|
extensions: []
|
99
104
|
extra_rdoc_files: []
|
@@ -119,6 +124,7 @@ files:
|
|
119
124
|
- lib/rack/protection/xss_header.rb
|
120
125
|
- rack-protection.gemspec
|
121
126
|
- spec/authenticity_token_spec.rb
|
127
|
+
- spec/base_spec.rb
|
122
128
|
- spec/escaped_params_spec.rb
|
123
129
|
- spec/form_token_spec.rb
|
124
130
|
- spec/frame_options_spec.rb
|
@@ -133,28 +139,28 @@ files:
|
|
133
139
|
- spec/spec_helper.rb
|
134
140
|
- spec/xss_header_spec.rb
|
135
141
|
homepage: http://github.com/rkh/rack-protection
|
136
|
-
licenses:
|
142
|
+
licenses:
|
143
|
+
- MIT
|
144
|
+
metadata: {}
|
137
145
|
post_install_message:
|
138
146
|
rdoc_options: []
|
139
147
|
require_paths:
|
140
148
|
- lib
|
141
149
|
required_ruby_version: !ruby/object:Gem::Requirement
|
142
|
-
none: false
|
143
150
|
requirements:
|
144
|
-
- -
|
151
|
+
- - '>='
|
145
152
|
- !ruby/object:Gem::Version
|
146
153
|
version: '0'
|
147
154
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
148
|
-
none: false
|
149
155
|
requirements:
|
150
|
-
- -
|
156
|
+
- - '>='
|
151
157
|
- !ruby/object:Gem::Version
|
152
158
|
version: '0'
|
153
159
|
requirements: []
|
154
160
|
rubyforge_project:
|
155
|
-
rubygems_version:
|
161
|
+
rubygems_version: 2.0.7
|
156
162
|
signing_key:
|
157
|
-
specification_version:
|
163
|
+
specification_version: 4
|
158
164
|
summary: You should use protection!
|
159
165
|
test_files: []
|
160
166
|
has_rdoc:
|