rack-protection 2.1.0 → 2.2.4
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
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 329a83327187635894cd3f8530c6e96b6cac29e8e28b1a8db0f1339cffa07a23
|
4
|
+
data.tar.gz: da8bea5ddeab7426d74fe977b7856a78c10f397da94b5769f5f72b83d5e0a1b1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6e4e35ae58ac6f131cff279bf7e68770eb87253d40c518980e1b44abbd9fbc7deb8952618e92289631d3641dd627ffb57f1963562536e8164aba2357544f36d6
|
7
|
+
data.tar.gz: 26b2c4e7413a6bf68386c474173ccad34ed7334064d4f262dfc0e04f546edc61b1de6ec95ca151544decbb1962957d85907734c38566b7e1b2501e6254d4956f
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'rack/protection'
|
2
2
|
require 'securerandom'
|
3
|
+
require 'openssl'
|
3
4
|
require 'base64'
|
4
5
|
|
5
6
|
module Rack
|
@@ -24,6 +25,13 @@ module Rack
|
|
24
25
|
# the token on a request. Default value:
|
25
26
|
# <tt>"authenticity_token"</tt>
|
26
27
|
#
|
28
|
+
# [<tt>:key</tt>] the name of the param that should contain
|
29
|
+
# the token in the session. Default value:
|
30
|
+
# <tt>:csrf</tt>
|
31
|
+
#
|
32
|
+
# [<tt>:allow_if</tt>] a proc for custom allow/deny logic. Default value:
|
33
|
+
# <tt>nil</tt>
|
34
|
+
#
|
27
35
|
# == Example: Forms application
|
28
36
|
#
|
29
37
|
# To show what the AuthenticityToken does, this section includes a sample
|
@@ -85,42 +93,57 @@ module Rack
|
|
85
93
|
TOKEN_LENGTH = 32
|
86
94
|
|
87
95
|
default_options :authenticity_param => 'authenticity_token',
|
96
|
+
:key => :csrf,
|
88
97
|
:allow_if => nil
|
89
98
|
|
90
|
-
def self.token(session)
|
91
|
-
self.new(nil).mask_authenticity_token(session)
|
99
|
+
def self.token(session, path: nil, method: :post)
|
100
|
+
self.new(nil).mask_authenticity_token(session, path: path, method: method)
|
92
101
|
end
|
93
102
|
|
94
103
|
def self.random_token
|
95
|
-
SecureRandom.
|
104
|
+
SecureRandom.urlsafe_base64(TOKEN_LENGTH, padding: false)
|
96
105
|
end
|
97
106
|
|
98
107
|
def accepts?(env)
|
99
|
-
session = session
|
108
|
+
session = session(env)
|
100
109
|
set_token(session)
|
101
110
|
|
102
111
|
safe?(env) ||
|
103
|
-
valid_token?(
|
104
|
-
valid_token?(
|
112
|
+
valid_token?(env, env['HTTP_X_CSRF_TOKEN']) ||
|
113
|
+
valid_token?(env, Request.new(env).params[options[:authenticity_param]]) ||
|
105
114
|
( options[:allow_if] && options[:allow_if].call(env) )
|
115
|
+
rescue
|
116
|
+
false
|
106
117
|
end
|
107
118
|
|
108
|
-
def mask_authenticity_token(session)
|
109
|
-
|
119
|
+
def mask_authenticity_token(session, path: nil, method: :post)
|
120
|
+
set_token(session)
|
121
|
+
|
122
|
+
token = if path && method
|
123
|
+
per_form_token(session, path, method)
|
124
|
+
else
|
125
|
+
global_token(session)
|
126
|
+
end
|
127
|
+
|
110
128
|
mask_token(token)
|
111
129
|
end
|
112
130
|
|
131
|
+
GLOBAL_TOKEN_IDENTIFIER = '!real_csrf_token'
|
132
|
+
private_constant :GLOBAL_TOKEN_IDENTIFIER
|
133
|
+
|
113
134
|
private
|
114
135
|
|
115
136
|
def set_token(session)
|
116
|
-
session[:
|
137
|
+
session[options[:key]] ||= self.class.random_token
|
117
138
|
end
|
118
139
|
|
119
140
|
# Checks the client's masked token to see if it matches the
|
120
141
|
# session token.
|
121
|
-
def valid_token?(
|
142
|
+
def valid_token?(env, token)
|
122
143
|
return false if token.nil? || token.empty?
|
123
144
|
|
145
|
+
session = session(env)
|
146
|
+
|
124
147
|
begin
|
125
148
|
token = decode_token(token)
|
126
149
|
rescue ArgumentError # encoded_masked_token is invalid Base64
|
@@ -131,13 +154,13 @@ module Rack
|
|
131
154
|
# to handle any unmasked tokens that we've issued without error.
|
132
155
|
|
133
156
|
if unmasked_token?(token)
|
134
|
-
compare_with_real_token
|
135
|
-
|
157
|
+
compare_with_real_token(token, session)
|
136
158
|
elsif masked_token?(token)
|
137
159
|
token = unmask_token(token)
|
138
160
|
|
139
|
-
|
140
|
-
|
161
|
+
compare_with_global_token(token, session) ||
|
162
|
+
compare_with_real_token(token, session) ||
|
163
|
+
compare_with_per_form_token(token, session, Request.new(env))
|
141
164
|
else
|
142
165
|
false # Token is malformed
|
143
166
|
end
|
@@ -147,7 +170,6 @@ module Rack
|
|
147
170
|
# on each request. The masking is used to mitigate SSL attacks
|
148
171
|
# like BREACH.
|
149
172
|
def mask_token(token)
|
150
|
-
token = decode_token(token)
|
151
173
|
one_time_pad = SecureRandom.random_bytes(token.length)
|
152
174
|
encrypted_token = xor_byte_strings(one_time_pad, token)
|
153
175
|
masked_token = one_time_pad + encrypted_token
|
@@ -176,16 +198,42 @@ module Rack
|
|
176
198
|
secure_compare(token, real_token(session))
|
177
199
|
end
|
178
200
|
|
201
|
+
def compare_with_global_token(token, session)
|
202
|
+
secure_compare(token, global_token(session))
|
203
|
+
end
|
204
|
+
|
205
|
+
def compare_with_per_form_token(token, session, request)
|
206
|
+
secure_compare(token,
|
207
|
+
per_form_token(session, request.path.chomp('/'), request.request_method)
|
208
|
+
)
|
209
|
+
end
|
210
|
+
|
179
211
|
def real_token(session)
|
180
|
-
decode_token(session[:
|
212
|
+
decode_token(session[options[:key]])
|
213
|
+
end
|
214
|
+
|
215
|
+
def global_token(session)
|
216
|
+
token_hmac(session, GLOBAL_TOKEN_IDENTIFIER)
|
217
|
+
end
|
218
|
+
|
219
|
+
def per_form_token(session, path, method)
|
220
|
+
token_hmac(session, "#{path}##{method.downcase}")
|
181
221
|
end
|
182
222
|
|
183
223
|
def encode_token(token)
|
184
|
-
Base64.
|
224
|
+
Base64.urlsafe_encode64(token)
|
185
225
|
end
|
186
226
|
|
187
227
|
def decode_token(token)
|
188
|
-
Base64.
|
228
|
+
Base64.urlsafe_decode64(token)
|
229
|
+
end
|
230
|
+
|
231
|
+
def token_hmac(session, identifier)
|
232
|
+
OpenSSL::HMAC.digest(
|
233
|
+
OpenSSL::Digest::SHA256.new,
|
234
|
+
real_token(session),
|
235
|
+
identifier
|
236
|
+
)
|
189
237
|
end
|
190
238
|
|
191
239
|
def xor_byte_strings(s1, s2)
|
@@ -34,7 +34,7 @@ module Rack
|
|
34
34
|
return true if options[:allow_if] && options[:allow_if].call(env)
|
35
35
|
|
36
36
|
if options.key? :origin_whitelist
|
37
|
-
warn "Rack::Protection origin_whitelist option is deprecated and will be removed, " \
|
37
|
+
warn env, "Rack::Protection origin_whitelist option is deprecated and will be removed, " \
|
38
38
|
"use permitted_origins instead.\n"
|
39
39
|
end
|
40
40
|
|
@@ -13,9 +13,11 @@ module Rack
|
|
13
13
|
|
14
14
|
def accepts?(env)
|
15
15
|
return true unless env.include? 'HTTP_X_FORWARDED_FOR'
|
16
|
-
|
17
|
-
|
18
|
-
return false if env.include?
|
16
|
+
|
17
|
+
ips = env['HTTP_X_FORWARDED_FOR'].split(',').map(&:strip)
|
18
|
+
return false if env.include?('HTTP_CLIENT_IP') && (!ips.include? env['HTTP_CLIENT_IP'])
|
19
|
+
return false if env.include?('HTTP_X_REAL_IP') && (!ips.include? env['HTTP_X_REAL_IP'])
|
20
|
+
|
19
21
|
true
|
20
22
|
end
|
21
23
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-protection
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- https://github.com/sinatra/sinatra/graphs/contributors
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-12-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -91,7 +91,7 @@ metadata:
|
|
91
91
|
source_code_uri: https://github.com/sinatra/sinatra/tree/master/rack-protection
|
92
92
|
homepage_uri: http://sinatrarb.com/protection/
|
93
93
|
documentation_uri: https://www.rubydoc.info/gems/rack-protection
|
94
|
-
post_install_message:
|
94
|
+
post_install_message:
|
95
95
|
rdoc_options: []
|
96
96
|
require_paths:
|
97
97
|
- lib
|
@@ -106,8 +106,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
106
106
|
- !ruby/object:Gem::Version
|
107
107
|
version: '0'
|
108
108
|
requirements: []
|
109
|
-
|
110
|
-
|
109
|
+
rubyforge_project:
|
110
|
+
rubygems_version: 2.7.6.3
|
111
|
+
signing_key:
|
111
112
|
specification_version: 4
|
112
113
|
summary: Protect against typical web attacks, works with all Rack apps, including
|
113
114
|
Rails.
|