rack-protection 2.0.7
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/Gemfile +13 -0
- data/License +23 -0
- data/README.md +118 -0
- data/Rakefile +72 -0
- data/lib/rack-protection.rb +1 -0
- data/lib/rack/protection.rb +54 -0
- data/lib/rack/protection/authenticity_token.rb +196 -0
- data/lib/rack/protection/base.rb +126 -0
- data/lib/rack/protection/content_security_policy.rb +80 -0
- data/lib/rack/protection/cookie_tossing.rb +75 -0
- data/lib/rack/protection/escaped_params.rb +89 -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 +40 -0
- data/lib/rack/protection/ip_spoofing.rb +23 -0
- data/lib/rack/protection/json_csrf.rb +57 -0
- data/lib/rack/protection/path_traversal.rb +42 -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/strict_transport.rb +39 -0
- data/lib/rack/protection/version.rb +5 -0
- data/lib/rack/protection/xss_header.rb +25 -0
- data/rack-protection.gemspec +40 -0
- metadata +114 -0
@@ -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,57 @@
|
|
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/0.10/security/#json-security
|
9
|
+
# http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx
|
10
|
+
#
|
11
|
+
# JSON GET APIs are vulnerable to being embedded as JavaScript when the
|
12
|
+
# Array prototype has been patched to track data. Checks the referrer
|
13
|
+
# even on GET requests if the content type is JSON.
|
14
|
+
#
|
15
|
+
# If request includes Origin HTTP header, defers to HttpOrigin to determine
|
16
|
+
# if the request is safe. Please refer to the documentation for more info.
|
17
|
+
#
|
18
|
+
# The `:allow_if` option can be set to a proc to use custom allow/deny logic.
|
19
|
+
class JsonCsrf < Base
|
20
|
+
default_options :allow_if => nil
|
21
|
+
|
22
|
+
alias react deny
|
23
|
+
|
24
|
+
def call(env)
|
25
|
+
request = Request.new(env)
|
26
|
+
status, headers, body = app.call(env)
|
27
|
+
|
28
|
+
if has_vector?(request, headers)
|
29
|
+
warn env, "attack prevented by #{self.class}"
|
30
|
+
|
31
|
+
react_and_close(env, body) or [status, headers, body]
|
32
|
+
else
|
33
|
+
[status, headers, body]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def has_vector?(request, headers)
|
38
|
+
return false if request.xhr?
|
39
|
+
return false if options[:allow_if] && options[:allow_if].call(request.env)
|
40
|
+
return false unless headers['Content-Type'].to_s.split(';', 2).first =~ /^\s*application\/json\s*$/
|
41
|
+
origin(request.env).nil? and referrer(request.env) != request.host
|
42
|
+
end
|
43
|
+
|
44
|
+
def react_and_close(env, body)
|
45
|
+
reaction = react(env)
|
46
|
+
|
47
|
+
close_body(body) if reaction
|
48
|
+
|
49
|
+
reaction
|
50
|
+
end
|
51
|
+
|
52
|
+
def close_body(body)
|
53
|
+
body.close if body.respond_to?(:close)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,42 @@
|
|
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
|
+
encoding = path.encoding
|
23
|
+
dot = '.'.encode(encoding)
|
24
|
+
slash = '/'.encode(encoding)
|
25
|
+
backslash = '\\'.encode(encoding)
|
26
|
+
|
27
|
+
parts = []
|
28
|
+
unescaped = path.gsub(/%2e/i, dot).gsub(/%2f/i, slash).gsub(/%5c/i, backslash)
|
29
|
+
unescaped = unescaped.gsub(backslash, slash)
|
30
|
+
|
31
|
+
unescaped.split(slash).each do |part|
|
32
|
+
next if part.empty? or part == dot
|
33
|
+
part == '..' ? parts.pop : parts << part
|
34
|
+
end
|
35
|
+
|
36
|
+
cleaned = slash + parts.join(slash)
|
37
|
+
cleaned << slash if parts.any? and unescaped =~ %r{/\.{0,2}$}
|
38
|
+
cleaned
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
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 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]
|
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,39 @@
|
|
1
|
+
require 'rack/protection'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
module Protection
|
5
|
+
##
|
6
|
+
# Prevented attack:: Protects against against protocol downgrade attacks and cookie hijacking.
|
7
|
+
# Supported browsers:: all
|
8
|
+
# More infos:: https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security
|
9
|
+
#
|
10
|
+
# browser will prevent any communications from being sent over HTTP
|
11
|
+
# to the specified domain and will instead send all communications over HTTPS.
|
12
|
+
# It also prevents HTTPS click through prompts on browsers.
|
13
|
+
#
|
14
|
+
# Options:
|
15
|
+
#
|
16
|
+
# max_age:: How long future requests to the domain should go over HTTPS; specified in seconds
|
17
|
+
# include_subdomains:: If all present and future subdomains will be HTTPS
|
18
|
+
# preload:: Allow this domain to be included in browsers HSTS preload list. See https://hstspreload.appspot.com/
|
19
|
+
|
20
|
+
class StrictTransport < Base
|
21
|
+
default_options :max_age => 31_536_000, :include_subdomains => false, :preload => false
|
22
|
+
|
23
|
+
def strict_transport
|
24
|
+
@strict_transport ||= begin
|
25
|
+
strict_transport = 'max-age=' + options[:max_age].to_s
|
26
|
+
strict_transport += '; includeSubDomains' if options[:include_subdomains]
|
27
|
+
strict_transport += '; preload' if options[:preload]
|
28
|
+
strict_transport.to_str
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def call(env)
|
33
|
+
status, headers, body = @app.call(env)
|
34
|
+
headers['Strict-Transport-Security'] ||= strict_transport
|
35
|
+
[status, headers, body]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
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 Chrome
|
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,40 @@
|
|
1
|
+
version = File.read(File.expand_path("../../VERSION", __FILE__)).strip
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
# general infos
|
5
|
+
s.name = "rack-protection"
|
6
|
+
s.version = version
|
7
|
+
s.description = "Protect against typical web attacks, works with all Rack apps, including Rails."
|
8
|
+
s.homepage = "http://sinatrarb.com/protection/"
|
9
|
+
s.summary = s.description
|
10
|
+
s.license = 'MIT'
|
11
|
+
s.authors = ["https://github.com/sinatra/sinatra/graphs/contributors"]
|
12
|
+
s.email = "sinatrarb@googlegroups.com"
|
13
|
+
s.files = Dir["lib/**/*.rb"] + [
|
14
|
+
"License",
|
15
|
+
"README.md",
|
16
|
+
"Rakefile",
|
17
|
+
"Gemfile",
|
18
|
+
"rack-protection.gemspec"
|
19
|
+
]
|
20
|
+
|
21
|
+
if s.respond_to?(:metadata)
|
22
|
+
s.metadata = {
|
23
|
+
'source_code_uri' => 'https://github.com/sinatra/sinatra/tree/master/rack-protection',
|
24
|
+
'homepage_uri' => 'http://sinatrarb.com/protection/',
|
25
|
+
'documentation_uri' => 'https://www.rubydoc.info/gems/rack-protection'
|
26
|
+
}
|
27
|
+
else
|
28
|
+
raise <<-EOF
|
29
|
+
RubyGems 2.0 or newer is required to protect against public gem pushes. You can update your rubygems version by running:
|
30
|
+
gem install rubygems-update
|
31
|
+
update_rubygems:
|
32
|
+
gem update --system
|
33
|
+
EOF
|
34
|
+
end
|
35
|
+
|
36
|
+
# dependencies
|
37
|
+
s.add_dependency "rack"
|
38
|
+
s.add_development_dependency "rack-test"
|
39
|
+
s.add_development_dependency "rspec", "~> 3.6"
|
40
|
+
end
|
metadata
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rack-protection
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.0.7
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- https://github.com/sinatra/sinatra/graphs/contributors
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-08-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rack
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rack-test
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.6'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.6'
|
55
|
+
description: Protect against typical web attacks, works with all Rack apps, including
|
56
|
+
Rails.
|
57
|
+
email: sinatrarb@googlegroups.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- Gemfile
|
63
|
+
- License
|
64
|
+
- README.md
|
65
|
+
- Rakefile
|
66
|
+
- lib/rack-protection.rb
|
67
|
+
- lib/rack/protection.rb
|
68
|
+
- lib/rack/protection/authenticity_token.rb
|
69
|
+
- lib/rack/protection/base.rb
|
70
|
+
- lib/rack/protection/content_security_policy.rb
|
71
|
+
- lib/rack/protection/cookie_tossing.rb
|
72
|
+
- lib/rack/protection/escaped_params.rb
|
73
|
+
- lib/rack/protection/form_token.rb
|
74
|
+
- lib/rack/protection/frame_options.rb
|
75
|
+
- lib/rack/protection/http_origin.rb
|
76
|
+
- lib/rack/protection/ip_spoofing.rb
|
77
|
+
- lib/rack/protection/json_csrf.rb
|
78
|
+
- lib/rack/protection/path_traversal.rb
|
79
|
+
- lib/rack/protection/remote_referrer.rb
|
80
|
+
- lib/rack/protection/remote_token.rb
|
81
|
+
- lib/rack/protection/session_hijacking.rb
|
82
|
+
- lib/rack/protection/strict_transport.rb
|
83
|
+
- lib/rack/protection/version.rb
|
84
|
+
- lib/rack/protection/xss_header.rb
|
85
|
+
- rack-protection.gemspec
|
86
|
+
homepage: http://sinatrarb.com/protection/
|
87
|
+
licenses:
|
88
|
+
- MIT
|
89
|
+
metadata:
|
90
|
+
source_code_uri: https://github.com/sinatra/sinatra/tree/master/rack-protection
|
91
|
+
homepage_uri: http://sinatrarb.com/protection/
|
92
|
+
documentation_uri: https://www.rubydoc.info/gems/rack-protection
|
93
|
+
post_install_message:
|
94
|
+
rdoc_options: []
|
95
|
+
require_paths:
|
96
|
+
- lib
|
97
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
requirements: []
|
108
|
+
rubyforge_project:
|
109
|
+
rubygems_version: 2.7.3
|
110
|
+
signing_key:
|
111
|
+
specification_version: 4
|
112
|
+
summary: Protect against typical web attacks, works with all Rack apps, including
|
113
|
+
Rails.
|
114
|
+
test_files: []
|