rack-protection-monkey 1.5.3
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.
- checksums.yaml +7 -0
- data/License +20 -0
- data/README.md +90 -0
- data/Rakefile +48 -0
- data/lib/rack-protection.rb +1 -0
- data/lib/rack/protection.rb +40 -0
- data/lib/rack/protection/authenticity_token.rb +31 -0
- data/lib/rack/protection/base.rb +121 -0
- data/lib/rack/protection/escaped_params.rb +87 -0
- data/lib/rack/protection/form_token.rb +23 -0
- data/lib/rack/protection/frame_options.rb +37 -0
- data/lib/rack/protection/http_origin.rb +34 -0
- data/lib/rack/protection/ip_spoofing.rb +23 -0
- data/lib/rack/protection/json_csrf.rb +35 -0
- data/lib/rack/protection/path_traversal.rb +47 -0
- data/lib/rack/protection/remote_referrer.rb +20 -0
- data/lib/rack/protection/remote_token.rb +22 -0
- data/lib/rack/protection/session_hijacking.rb +36 -0
- data/lib/rack/protection/version.rb +16 -0
- data/lib/rack/protection/xss_header.rb +25 -0
- data/rack-protection.gemspec +123 -0
- data/spec/lib/rack/protection/authenticity_token_spec.rb +46 -0
- data/spec/lib/rack/protection/base_spec.rb +38 -0
- data/spec/lib/rack/protection/escaped_params_spec.rb +41 -0
- data/spec/lib/rack/protection/form_token_spec.rb +31 -0
- data/spec/lib/rack/protection/frame_options_spec.rb +37 -0
- data/spec/lib/rack/protection/http_origin_spec.rb +40 -0
- data/spec/lib/rack/protection/ip_spoofing_spec.rb +33 -0
- data/spec/lib/rack/protection/json_csrf_spec.rb +56 -0
- data/spec/lib/rack/protection/path_traversal_spec.rb +39 -0
- data/spec/lib/rack/protection/protection_spec.rb +103 -0
- data/spec/lib/rack/protection/remote_referrer_spec.rb +29 -0
- data/spec/lib/rack/protection/remote_token_spec.rb +40 -0
- data/spec/lib/rack/protection/session_hijacking_spec.rb +53 -0
- data/spec/lib/rack/protection/xss_header_spec.rb +54 -0
- data/spec/spec_helper.rb +86 -0
- data/spec/support/dummy_app.rb +7 -0
- data/spec/support/not_implemented_as_pending.rb +23 -0
- data/spec/support/rack_monkey_patches.rb +21 -0
- data/spec/support/shared_examples.rb +65 -0
- data/spec/support/spec_helpers.rb +36 -0
- metadata +180 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rack/protection'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
module Protection
|
5
|
+
##
|
6
|
+
# Prevented attack:: CSRF
|
7
|
+
# Supported browsers:: all
|
8
|
+
# More infos:: http://en.wikipedia.org/wiki/Cross-site_request_forgery
|
9
|
+
#
|
10
|
+
# Only accepts submitted forms if a given access token matches the token
|
11
|
+
# included in the session. Does not expect such a token from Ajax request.
|
12
|
+
#
|
13
|
+
# This middleware is not used when using the Rack::Protection collection,
|
14
|
+
# since it might be a security issue, depending on your application
|
15
|
+
#
|
16
|
+
# Compatible with Rails and rack-csrf.
|
17
|
+
class FormToken < AuthenticityToken
|
18
|
+
def accepts?(env)
|
19
|
+
env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest" or super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'rack/protection'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
module Protection
|
5
|
+
##
|
6
|
+
# Prevented attack:: Clickjacking
|
7
|
+
# Supported browsers:: Internet Explorer 8, Firefox 3.6.9, Opera 10.50,
|
8
|
+
# Safari 4.0, Chrome 4.1.249.1042 and later
|
9
|
+
# More infos:: https://developer.mozilla.org/en/The_X-FRAME-OPTIONS_response_header
|
10
|
+
#
|
11
|
+
# Sets X-Frame-Options header to tell the browser avoid embedding the page
|
12
|
+
# in a frame.
|
13
|
+
#
|
14
|
+
# Options:
|
15
|
+
#
|
16
|
+
# frame_options:: Defines who should be allowed to embed the page in a
|
17
|
+
# frame. Use :deny to forbid any embedding, :sameorigin
|
18
|
+
# to allow embedding from the same origin (default).
|
19
|
+
class FrameOptions < Base
|
20
|
+
default_options :frame_options => :sameorigin
|
21
|
+
|
22
|
+
def frame_options
|
23
|
+
@frame_options ||= begin
|
24
|
+
frame_options = options[:frame_options]
|
25
|
+
frame_options = options[:frame_options].to_s.upcase unless frame_options.respond_to? :to_str
|
26
|
+
frame_options.to_str
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def call(env)
|
31
|
+
status, headers, body = @app.call(env)
|
32
|
+
headers['X-Frame-Options'] ||= frame_options if html? headers
|
33
|
+
[status, headers, body]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'rack/protection'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
module Protection
|
5
|
+
##
|
6
|
+
# Prevented attack:: CSRF
|
7
|
+
# Supported browsers:: Google Chrome 2, Safari 4 and later
|
8
|
+
# More infos:: http://en.wikipedia.org/wiki/Cross-site_request_forgery
|
9
|
+
# http://tools.ietf.org/html/draft-abarth-origin
|
10
|
+
#
|
11
|
+
# Does not accept unsafe HTTP requests when value of Origin HTTP request header
|
12
|
+
# does not match default or whitelisted URIs.
|
13
|
+
class HttpOrigin < Base
|
14
|
+
DEFAULT_PORTS = { 'http' => 80, 'https' => 443, 'coffee' => 80 }
|
15
|
+
default_reaction :deny
|
16
|
+
|
17
|
+
def base_url(env)
|
18
|
+
request = Rack::Request.new(env)
|
19
|
+
port = ":#{request.port}" unless request.port == DEFAULT_PORTS[request.scheme]
|
20
|
+
"#{request.scheme}://#{request.host}#{port}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def accepts?(env)
|
24
|
+
origin = env['HTTP_ORIGIN']
|
25
|
+
|
26
|
+
return true if safe? env
|
27
|
+
return true unless origin && origin != 'null'
|
28
|
+
return true if base_url(env) == origin
|
29
|
+
Array(options[:origin_whitelist]).include? origin
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rack/protection'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
module Protection
|
5
|
+
##
|
6
|
+
# Prevented attack:: IP spoofing
|
7
|
+
# Supported browsers:: all
|
8
|
+
# More infos:: http://blog.c22.cc/2011/04/22/surveymonkey-ip-spoofing/
|
9
|
+
#
|
10
|
+
# Detect (some) IP spoofing attacks.
|
11
|
+
class IPSpoofing < Base
|
12
|
+
default_reaction :deny
|
13
|
+
|
14
|
+
def accepts?(env)
|
15
|
+
return true unless env.include? 'HTTP_X_FORWARDED_FOR'
|
16
|
+
ips = env['HTTP_X_FORWARDED_FOR'].split(/\s*,\s*/)
|
17
|
+
return false if env.include? 'HTTP_CLIENT_IP' and not ips.include? env['HTTP_CLIENT_IP']
|
18
|
+
return false if env.include? 'HTTP_X_REAL_IP' and not ips.include? env['HTTP_X_REAL_IP']
|
19
|
+
true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'rack/protection'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
module Protection
|
5
|
+
##
|
6
|
+
# Prevented attack:: CSRF
|
7
|
+
# Supported browsers:: all
|
8
|
+
# More infos:: http://flask.pocoo.org/docs/security/#json-security
|
9
|
+
#
|
10
|
+
# JSON GET APIs are vulnerable to being embedded as JavaScript while the
|
11
|
+
# Array prototype has been patched to track data. Checks the referrer
|
12
|
+
# even on GET requests if the content type is JSON.
|
13
|
+
class JsonCsrf < Base
|
14
|
+
alias react deny
|
15
|
+
|
16
|
+
def call(env)
|
17
|
+
request = Request.new(env)
|
18
|
+
status, headers, body = app.call(env)
|
19
|
+
|
20
|
+
if has_vector? request, headers
|
21
|
+
warn env, "attack prevented by #{self.class}"
|
22
|
+
react(env) or [status, headers, body]
|
23
|
+
else
|
24
|
+
[status, headers, body]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def has_vector?(request, headers)
|
29
|
+
return false if request.xhr?
|
30
|
+
return false unless headers['Content-Type'].to_s.split(';', 2).first =~ /^\s*application\/json\s*$/
|
31
|
+
origin(request.env).nil? and referrer(request.env) != request.host
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'rack/protection'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
module Protection
|
5
|
+
##
|
6
|
+
# Prevented attack:: Directory traversal
|
7
|
+
# Supported browsers:: all
|
8
|
+
# More infos:: http://en.wikipedia.org/wiki/Directory_traversal
|
9
|
+
#
|
10
|
+
# Unescapes '/' and '.', expands +path_info+.
|
11
|
+
# Thus <tt>GET /foo/%2e%2e%2fbar</tt> becomes <tt>GET /bar</tt>.
|
12
|
+
class PathTraversal < Base
|
13
|
+
def call(env)
|
14
|
+
path_was = env["PATH_INFO"]
|
15
|
+
env["PATH_INFO"] = cleanup path_was if path_was && !path_was.empty?
|
16
|
+
app.call env
|
17
|
+
ensure
|
18
|
+
env["PATH_INFO"] = path_was
|
19
|
+
end
|
20
|
+
|
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
|
+
|
33
|
+
parts = []
|
34
|
+
unescaped = path.gsub(/%2e/i, dot).gsub(/%2f/i, slash)
|
35
|
+
|
36
|
+
unescaped.split(slash).each do |part|
|
37
|
+
next if part.empty? or part == dot
|
38
|
+
part == '..' ? parts.pop : parts << part
|
39
|
+
end
|
40
|
+
|
41
|
+
cleaned = slash + parts.join(slash)
|
42
|
+
cleaned << slash if parts.any? and unescaped =~ %r{/\.{0,2}$}
|
43
|
+
cleaned
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rack/protection'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
module Protection
|
5
|
+
##
|
6
|
+
# Prevented attack:: CSRF
|
7
|
+
# Supported browsers:: all
|
8
|
+
# More infos:: http://en.wikipedia.org/wiki/Cross-site_request_forgery
|
9
|
+
#
|
10
|
+
# Does not accept unsafe HTTP requests if the Referer [sic] header is set to
|
11
|
+
# a different host.
|
12
|
+
class RemoteReferrer < Base
|
13
|
+
default_reaction :deny
|
14
|
+
|
15
|
+
def accepts?(env)
|
16
|
+
safe?(env) or referrer(env) == Request.new(env).host
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rack/protection'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
module Protection
|
5
|
+
##
|
6
|
+
# Prevented attack:: CSRF
|
7
|
+
# Supported browsers:: all
|
8
|
+
# More infos:: http://en.wikipedia.org/wiki/Cross-site_request_forgery
|
9
|
+
#
|
10
|
+
# Only accepts unsafe HTTP requests if a given access token matches the token
|
11
|
+
# included in the session *or* the request comes from the same origin.
|
12
|
+
#
|
13
|
+
# Compatible with Rails and rack-csrf.
|
14
|
+
class RemoteToken < AuthenticityToken
|
15
|
+
default_reaction :deny
|
16
|
+
|
17
|
+
def accepts?(env)
|
18
|
+
super or referrer(env) == Request.new(env).host
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'rack/protection'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
module Protection
|
5
|
+
##
|
6
|
+
# Prevented attack:: Session Hijacking
|
7
|
+
# Supported browsers:: all
|
8
|
+
# More infos:: http://en.wikipedia.org/wiki/Session_hijacking
|
9
|
+
#
|
10
|
+
# Tracks request properties like the user agent in the session and empties
|
11
|
+
# the session if those properties change. This essentially prevents attacks
|
12
|
+
# from Firesheep. Since all headers taken into consideration can be
|
13
|
+
# spoofed, too, this will not prevent determined hijacking attempts.
|
14
|
+
class SessionHijacking < Base
|
15
|
+
default_reaction :drop_session
|
16
|
+
default_options :tracking_key => :tracking, :encrypt_tracking => true,
|
17
|
+
:track => %w[HTTP_USER_AGENT HTTP_ACCEPT_LANGUAGE]
|
18
|
+
|
19
|
+
def accepts?(env)
|
20
|
+
session = session env
|
21
|
+
key = options[:tracking_key]
|
22
|
+
if session.include? key
|
23
|
+
session[key].all? { |k,v| v == encrypt(env[k]) }
|
24
|
+
else
|
25
|
+
session[key] = {}
|
26
|
+
options[:track].each { |k| session[key][k] = encrypt(env[k]) }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def encrypt(value)
|
31
|
+
value = value.to_s.downcase
|
32
|
+
options[:encrypt_tracking] ? super(value) : value
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Rack
|
2
|
+
module Protection
|
3
|
+
def self.version
|
4
|
+
VERSION
|
5
|
+
end
|
6
|
+
|
7
|
+
SIGNATURE = [1, 5, 3]
|
8
|
+
VERSION = SIGNATURE.join('.')
|
9
|
+
|
10
|
+
VERSION.extend Comparable
|
11
|
+
def VERSION.<=>(other)
|
12
|
+
other = other.split('.').map { |i| i.to_i } if other.respond_to? :split
|
13
|
+
SIGNATURE <=> Array(other)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rack/protection'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
module Protection
|
5
|
+
##
|
6
|
+
# Prevented attack:: Non-permanent XSS
|
7
|
+
# Supported browsers:: Internet Explorer 8 and later
|
8
|
+
# More infos:: http://blogs.msdn.com/b/ie/archive/2008/07/01/ie8-security-part-iv-the-xss-filter.aspx
|
9
|
+
#
|
10
|
+
# Sets X-XSS-Protection header to tell the browser to block attacks.
|
11
|
+
#
|
12
|
+
# Options:
|
13
|
+
# xss_mode:: How the browser should prevent the attack (default: :block)
|
14
|
+
class XSSHeader < Base
|
15
|
+
default_options :xss_mode => :block, :nosniff => true
|
16
|
+
|
17
|
+
def call(env)
|
18
|
+
status, headers, body = @app.call(env)
|
19
|
+
headers['X-XSS-Protection'] ||= "1; mode=#{options[:xss_mode]}" if html? headers
|
20
|
+
headers['X-Content-Type-Options'] ||= 'nosniff' if options[:nosniff]
|
21
|
+
[status, headers, body]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# Run `rake rack-protection.gemspec` to update the gemspec.
|
2
|
+
Gem::Specification.new do |s|
|
3
|
+
# general infos
|
4
|
+
s.name = "rack-protection-monkey"
|
5
|
+
s.version = "1.5.3"
|
6
|
+
s.description = "You should use protection! - Monkey Version"
|
7
|
+
s.homepage = "http://github.com/sinatra/rack-protection"
|
8
|
+
s.summary = s.description
|
9
|
+
s.license = 'MIT'
|
10
|
+
|
11
|
+
# generated from git shortlog -sn
|
12
|
+
s.authors = [
|
13
|
+
"Konstantin Haase",
|
14
|
+
"Alex Rodionov",
|
15
|
+
"Patrick Ellis",
|
16
|
+
"Jason Staten",
|
17
|
+
"ITO Nobuaki",
|
18
|
+
"Jeff Welling",
|
19
|
+
"Matteo Centenaro",
|
20
|
+
"Egor Homakov",
|
21
|
+
"Florian Gilcher",
|
22
|
+
"Fojas",
|
23
|
+
"Igor Bochkariov",
|
24
|
+
"Mael Clerambault",
|
25
|
+
"Martin Mauch",
|
26
|
+
"Renne Nissinen",
|
27
|
+
"SAKAI, Kazuaki",
|
28
|
+
"Stanislav Savulchik",
|
29
|
+
"Steve Agalloco",
|
30
|
+
"TOBY",
|
31
|
+
"Thais Camilo and Konstantin Haase",
|
32
|
+
"Vipul A M",
|
33
|
+
"Akzhan Abdulin",
|
34
|
+
"brookemckim",
|
35
|
+
"Bj\u{f8}rge N\u{e6}ss",
|
36
|
+
"Chris Heald",
|
37
|
+
"Chris Mytton",
|
38
|
+
"Corey Ward",
|
39
|
+
"Dario Cravero",
|
40
|
+
"David Kellum"
|
41
|
+
]
|
42
|
+
|
43
|
+
# generated from git shortlog -sne
|
44
|
+
s.email = [
|
45
|
+
"konstantin.mailinglists@googlemail.com",
|
46
|
+
"p0deje@gmail.com",
|
47
|
+
"jstaten07@gmail.com",
|
48
|
+
"patrick@soundcloud.com",
|
49
|
+
"jeff.welling@gmail.com",
|
50
|
+
"bugant@gmail.com",
|
51
|
+
"daydream.trippers@gmail.com",
|
52
|
+
"florian.gilcher@asquera.de",
|
53
|
+
"developer@fojasaur.us",
|
54
|
+
"ujifgc@gmail.com",
|
55
|
+
"mael@clerambault.fr",
|
56
|
+
"martin.mauch@gmail.com",
|
57
|
+
"rennex@iki.fi",
|
58
|
+
"kaz.july.7@gmail.com",
|
59
|
+
"s.savulchik@gmail.com",
|
60
|
+
"steve.agalloco@gmail.com",
|
61
|
+
"toby.net.info.mail+git@gmail.com",
|
62
|
+
"dev+narwen+rkh@rkh.im",
|
63
|
+
"vipulnsward@gmail.com",
|
64
|
+
"akzhan.abdulin@gmail.com",
|
65
|
+
"brooke@digitalocean.com",
|
66
|
+
"bjoerge@bengler.no",
|
67
|
+
"cheald@gmail.com",
|
68
|
+
"self@hecticjeff.net",
|
69
|
+
"coreyward@me.com",
|
70
|
+
"dario@uxtemple.com",
|
71
|
+
"dek-oss@gravitext.com",
|
72
|
+
"homakov@gmail.com"
|
73
|
+
]
|
74
|
+
|
75
|
+
# generated from git ls-files
|
76
|
+
s.files = [
|
77
|
+
"License",
|
78
|
+
"README.md",
|
79
|
+
"Rakefile",
|
80
|
+
"lib/rack-protection.rb",
|
81
|
+
"lib/rack/protection.rb",
|
82
|
+
"lib/rack/protection/authenticity_token.rb",
|
83
|
+
"lib/rack/protection/base.rb",
|
84
|
+
"lib/rack/protection/escaped_params.rb",
|
85
|
+
"lib/rack/protection/form_token.rb",
|
86
|
+
"lib/rack/protection/frame_options.rb",
|
87
|
+
"lib/rack/protection/http_origin.rb",
|
88
|
+
"lib/rack/protection/ip_spoofing.rb",
|
89
|
+
"lib/rack/protection/json_csrf.rb",
|
90
|
+
"lib/rack/protection/path_traversal.rb",
|
91
|
+
"lib/rack/protection/remote_referrer.rb",
|
92
|
+
"lib/rack/protection/remote_token.rb",
|
93
|
+
"lib/rack/protection/session_hijacking.rb",
|
94
|
+
"lib/rack/protection/version.rb",
|
95
|
+
"lib/rack/protection/xss_header.rb",
|
96
|
+
"rack-protection.gemspec",
|
97
|
+
"spec/lib/rack/protection/authenticity_token_spec.rb",
|
98
|
+
"spec/lib/rack/protection/base_spec.rb",
|
99
|
+
"spec/lib/rack/protection/escaped_params_spec.rb",
|
100
|
+
"spec/lib/rack/protection/form_token_spec.rb",
|
101
|
+
"spec/lib/rack/protection/frame_options_spec.rb",
|
102
|
+
"spec/lib/rack/protection/http_origin_spec.rb",
|
103
|
+
"spec/lib/rack/protection/ip_spoofing_spec.rb",
|
104
|
+
"spec/lib/rack/protection/json_csrf_spec.rb",
|
105
|
+
"spec/lib/rack/protection/path_traversal_spec.rb",
|
106
|
+
"spec/lib/rack/protection/protection_spec.rb",
|
107
|
+
"spec/lib/rack/protection/remote_referrer_spec.rb",
|
108
|
+
"spec/lib/rack/protection/remote_token_spec.rb",
|
109
|
+
"spec/lib/rack/protection/session_hijacking_spec.rb",
|
110
|
+
"spec/lib/rack/protection/xss_header_spec.rb",
|
111
|
+
"spec/spec_helper.rb",
|
112
|
+
"spec/support/dummy_app.rb",
|
113
|
+
"spec/support/not_implemented_as_pending.rb",
|
114
|
+
"spec/support/rack_monkey_patches.rb",
|
115
|
+
"spec/support/shared_examples.rb",
|
116
|
+
"spec/support/spec_helpers.rb"
|
117
|
+
]
|
118
|
+
|
119
|
+
# dependencies
|
120
|
+
s.add_dependency "rack"
|
121
|
+
s.add_development_dependency "rack-test"
|
122
|
+
s.add_development_dependency "rspec", "~> 3.0.0"
|
123
|
+
end
|