Class: Rack::Protection::Base
- Inherits:
-
Object
- Object
- Rack::Protection::Base
show all
- Defined in:
- lib/rack/protection/base.rb
Direct Known Subclasses
AuthenticityToken, ContentSecurityPolicy, CookieTossing, EscapedParams, FrameOptions, HostAuthorization, HttpOrigin, IPSpoofing, JsonCsrf, PathTraversal, ReferrerPolicy, RemoteReferrer, SessionHijacking, StrictTransport, XSSHeader
Constant Summary
collapse
- DEFAULT_OPTIONS =
{
reaction: :default_reaction, logging: true,
message: 'Forbidden', encryptor: Digest::SHA1,
session_key: 'rack.session', status: 403,
allow_empty_referrer: true,
report_key: 'protection.failed',
html_types: %w[text/html application/xhtml text/xml application/xml]
}
Instance Attribute Summary collapse
Class Method Summary
collapse
Instance Method Summary
collapse
Constructor Details
#initialize(app, options = {}) ⇒ Base
Returns a new instance of Base.
35
36
37
38
|
# File 'lib/rack/protection/base.rb', line 35
def initialize(app, options = {})
@app = app
@options = default_options.merge(options)
end
|
Instance Attribute Details
#app ⇒ Object
Returns the value of attribute app.
21
22
23
|
# File 'lib/rack/protection/base.rb', line 21
def app
@app
end
|
#options ⇒ Object
Returns the value of attribute options.
21
22
23
|
# File 'lib/rack/protection/base.rb', line 21
def options
@options
end
|
Class Method Details
.default_options(options) ⇒ Object
23
24
25
|
# File 'lib/rack/protection/base.rb', line 23
def self.default_options(options)
define_method(:default_options) { super().merge(options) }
end
|
.default_reaction(reaction) ⇒ Object
27
28
29
|
# File 'lib/rack/protection/base.rb', line 27
def self.default_reaction(reaction)
alias_method(:default_reaction, reaction)
end
|
Instance Method Details
#accepts?(env) ⇒ Boolean
44
45
46
|
# File 'lib/rack/protection/base.rb', line 44
def accepts?(env)
raise NotImplementedError, "#{self.class} implementation pending"
end
|
#call(env) ⇒ Object
48
49
50
51
52
53
54
|
# File 'lib/rack/protection/base.rb', line 48
def call(env)
unless accepts? env
instrument env
result = react env
end
result or app.call(env)
end
|
#debug(env, message) ⇒ Object
61
62
63
64
65
66
|
# File 'lib/rack/protection/base.rb', line 61
def debug(env, message)
return unless options[:logging]
l = options[:logger] || env['rack.logger'] || ::Logger.new(env['rack.errors'])
l.debug(message)
end
|
#default_options ⇒ Object
31
32
33
|
# File 'lib/rack/protection/base.rb', line 31
def default_options
DEFAULT_OPTIONS
end
|
#deny(env) ⇒ Object
Also known as:
default_reaction
82
83
84
85
|
# File 'lib/rack/protection/base.rb', line 82
def deny(env)
warn env, "attack prevented by #{self.class}"
[options[:status], { 'content-type' => 'text/plain' }, [options[:message]]]
end
|
#drop_session(env) ⇒ Object
102
103
104
105
106
107
108
109
110
|
# File 'lib/rack/protection/base.rb', line 102
def drop_session(env)
return unless session? env
session(env).clear
return if ["1", "true"].include?(ENV["RACK_PROTECTION_SILENCE_DROP_SESSION_WARNING"])
warn env, "session dropped by #{self.class}"
end
|
#encrypt(value) ⇒ Object
130
131
132
|
# File 'lib/rack/protection/base.rb', line 130
def encrypt(value)
options[:encryptor].hexdigest value.to_s
end
|
#html?(headers) ⇒ Boolean
140
141
142
143
144
|
# File 'lib/rack/protection/base.rb', line 140
def html?()
return false unless ( = .detect { |k, _v| k.downcase == 'content-type' })
options[:html_types].include? .last[%r{^\w+/\w+}]
end
|
#instrument(env) ⇒ Object
75
76
77
78
79
80
|
# File 'lib/rack/protection/base.rb', line 75
def instrument(env)
return unless (i = options[:instrumenter])
env['rack.protection.attack'] = self.class.name.split('::').last.downcase
i.instrument('rack.protection', env)
end
|
#origin(env) ⇒ Object
120
121
122
|
# File 'lib/rack/protection/base.rb', line 120
def origin(env)
env['HTTP_ORIGIN'] || env['HTTP_X_ORIGIN']
end
|
#random_string(secure = defined? SecureRandom)) ⇒ Object
124
125
126
127
128
|
# File 'lib/rack/protection/base.rb', line 124
def random_string(secure = defined? SecureRandom)
secure ? SecureRandom.hex(16) : '%032x' % rand((2**128) - 1)
rescue NotImplementedError
random_string false
end
|
#react(env) ⇒ Object
56
57
58
59
|
# File 'lib/rack/protection/base.rb', line 56
def react(env)
result = send(options[:reaction], env)
result if (Array === result) && (result.size == 3)
end
|
#referrer(env) ⇒ Object
112
113
114
115
116
117
118
|
# File 'lib/rack/protection/base.rb', line 112
def referrer(env)
ref = env['HTTP_REFERER'].to_s
return if !options[:allow_empty_referrer] && ref.empty?
URI.parse(ref).host || Request.new(env).host
rescue URI::InvalidURIError
end
|
#report(env) ⇒ Object
87
88
89
90
|
# File 'lib/rack/protection/base.rb', line 87
def report(env)
warn env, "attack reported by #{self.class}"
env[options[:report_key]] = true
end
|
#safe?(env) ⇒ Boolean
40
41
42
|
# File 'lib/rack/protection/base.rb', line 40
def safe?(env)
%w[GET HEAD OPTIONS TRACE].include? env['REQUEST_METHOD']
end
|
#secure_compare(a, b) ⇒ Object
134
135
136
|
# File 'lib/rack/protection/base.rb', line 134
def secure_compare(a, b)
Rack::Utils.secure_compare(a.to_s, b.to_s)
end
|
#session(env) ⇒ Object
96
97
98
99
100
|
# File 'lib/rack/protection/base.rb', line 96
def session(env)
return env[options[:session_key]] if session? env
raise "you need to set up a session middleware *before* #{self.class}"
end
|
#session?(env) ⇒ Boolean
92
93
94
|
# File 'lib/rack/protection/base.rb', line 92
def session?(env)
env.include? options[:session_key]
end
|
#warn(env, message) ⇒ Object
68
69
70
71
72
73
|
# File 'lib/rack/protection/base.rb', line 68
def warn(env, message)
return unless options[:logging]
l = options[:logger] || env['rack.logger'] || ::Logger.new(env['rack.errors'])
l.warn(message)
end
|