rack-protection 2.0.7
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|