rack-protection 2.0.0.beta2 → 2.0.0.rc1
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.
Potentially problematic release.
This version of rack-protection might be problematic. Click here for more details.
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7866e19271188b3c5668af566e5d61f305d7ebfd
|
4
|
+
data.tar.gz: 3cbee3fe323d7dbf531741731cda60dfe76b451e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d740e0613f51e92b942e33611f29f317dd197be142818724c67a06be9536dbdba2d1e7b890d0f7444db6c804e4e77d9f156ab392ab503481985bd7481e6d3beb
|
7
|
+
data.tar.gz: ca24a69a0ef6b19bb3087d6c60f70ff51043e3e8ab7da691ab2311d2d7da73129e24c5adacbd37ce9b9436ee8541f29b2c96d98b6276e8373f00d0754860ed08
|
@@ -12,65 +12,28 @@ module Rack
|
|
12
12
|
# Only accepts unsafe HTTP requests if a given access token matches the token
|
13
13
|
# included in the session.
|
14
14
|
#
|
15
|
-
# Compatible with
|
15
|
+
# Compatible with rack-csrf.
|
16
16
|
#
|
17
17
|
# Options:
|
18
18
|
#
|
19
19
|
# authenticity_param: Defines the param's name that should contain the token on a request.
|
20
|
-
#
|
21
20
|
class AuthenticityToken < Base
|
21
|
+
TOKEN_LENGTH = 32
|
22
|
+
|
22
23
|
default_options :authenticity_param => 'authenticity_token',
|
23
|
-
:authenticity_token_length => 32,
|
24
24
|
:allow_if => nil
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
end
|
30
|
-
|
31
|
-
def random_token(length = 32)
|
32
|
-
SecureRandom.base64(length)
|
33
|
-
end
|
34
|
-
|
35
|
-
# Creates a masked version of the authenticity token that varies
|
36
|
-
# on each request. The masking is used to mitigate SSL attacks
|
37
|
-
# like BREACH.
|
38
|
-
def mask_token(token)
|
39
|
-
token = decode_token(token)
|
40
|
-
one_time_pad = SecureRandom.random_bytes(token.length)
|
41
|
-
encrypted_token = xor_byte_strings(one_time_pad, token)
|
42
|
-
masked_token = one_time_pad + encrypted_token
|
43
|
-
encode_token masked_token
|
44
|
-
end
|
45
|
-
|
46
|
-
# Essentially the inverse of +mask_token+.
|
47
|
-
def unmask_decoded_token(masked_token)
|
48
|
-
# Split the token into the one-time pad and the encrypted
|
49
|
-
# value and decrypt it
|
50
|
-
token_length = masked_token.length / 2
|
51
|
-
one_time_pad = masked_token[0...token_length]
|
52
|
-
encrypted_token = masked_token[token_length..-1]
|
53
|
-
xor_byte_strings(one_time_pad, encrypted_token)
|
54
|
-
end
|
55
|
-
|
56
|
-
def encode_token(token)
|
57
|
-
Base64.strict_encode64(token)
|
58
|
-
end
|
59
|
-
|
60
|
-
def decode_token(token)
|
61
|
-
Base64.strict_decode64(token)
|
62
|
-
end
|
63
|
-
|
64
|
-
private
|
26
|
+
def self.token(session)
|
27
|
+
self.new(nil).mask_authenticity_token(session)
|
28
|
+
end
|
65
29
|
|
66
|
-
|
67
|
-
|
68
|
-
end
|
30
|
+
def self.random_token
|
31
|
+
SecureRandom.base64(TOKEN_LENGTH)
|
69
32
|
end
|
70
33
|
|
71
34
|
def accepts?(env)
|
72
35
|
session = session env
|
73
|
-
session
|
36
|
+
set_token(session)
|
74
37
|
|
75
38
|
safe?(env) ||
|
76
39
|
valid_token?(session, env['HTTP_X_CSRF_TOKEN']) ||
|
@@ -78,10 +41,15 @@ module Rack
|
|
78
41
|
( options[:allow_if] && options[:allow_if].call(env) )
|
79
42
|
end
|
80
43
|
|
44
|
+
def mask_authenticity_token(session)
|
45
|
+
token = set_token(session)
|
46
|
+
mask_token(token)
|
47
|
+
end
|
48
|
+
|
81
49
|
private
|
82
50
|
|
83
|
-
def
|
84
|
-
|
51
|
+
def set_token(session)
|
52
|
+
session[:csrf] ||= self.class.random_token
|
85
53
|
end
|
86
54
|
|
87
55
|
# Checks the client's masked token to see if it matches the
|
@@ -90,7 +58,7 @@ module Rack
|
|
90
58
|
return false if token.nil? || token.empty?
|
91
59
|
|
92
60
|
begin
|
93
|
-
token =
|
61
|
+
token = decode_token(token)
|
94
62
|
rescue ArgumentError # encoded_masked_token is invalid Base64
|
95
63
|
return false
|
96
64
|
end
|
@@ -102,7 +70,7 @@ module Rack
|
|
102
70
|
compare_with_real_token token, session
|
103
71
|
|
104
72
|
elsif masked_token?(token)
|
105
|
-
token =
|
73
|
+
token = unmask_token(token)
|
106
74
|
|
107
75
|
compare_with_real_token token, session
|
108
76
|
|
@@ -111,12 +79,33 @@ module Rack
|
|
111
79
|
end
|
112
80
|
end
|
113
81
|
|
82
|
+
# Creates a masked version of the authenticity token that varies
|
83
|
+
# on each request. The masking is used to mitigate SSL attacks
|
84
|
+
# like BREACH.
|
85
|
+
def mask_token(token)
|
86
|
+
token = decode_token(token)
|
87
|
+
one_time_pad = SecureRandom.random_bytes(token.length)
|
88
|
+
encrypted_token = xor_byte_strings(one_time_pad, token)
|
89
|
+
masked_token = one_time_pad + encrypted_token
|
90
|
+
encode_token(masked_token)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Essentially the inverse of +mask_token+.
|
94
|
+
def unmask_token(masked_token)
|
95
|
+
# Split the token into the one-time pad and the encrypted
|
96
|
+
# value and decrypt it
|
97
|
+
token_length = masked_token.length / 2
|
98
|
+
one_time_pad = masked_token[0...token_length]
|
99
|
+
encrypted_token = masked_token[token_length..-1]
|
100
|
+
xor_byte_strings(one_time_pad, encrypted_token)
|
101
|
+
end
|
102
|
+
|
114
103
|
def unmasked_token?(token)
|
115
|
-
token.length ==
|
104
|
+
token.length == TOKEN_LENGTH
|
116
105
|
end
|
117
106
|
|
118
107
|
def masked_token?(token)
|
119
|
-
token.length ==
|
108
|
+
token.length == TOKEN_LENGTH * 2
|
120
109
|
end
|
121
110
|
|
122
111
|
def compare_with_real_token(token, session)
|
@@ -124,7 +113,19 @@ module Rack
|
|
124
113
|
end
|
125
114
|
|
126
115
|
def real_token(session)
|
127
|
-
|
116
|
+
decode_token(session[:csrf])
|
117
|
+
end
|
118
|
+
|
119
|
+
def encode_token(token)
|
120
|
+
Base64.strict_encode64(token)
|
121
|
+
end
|
122
|
+
|
123
|
+
def decode_token(token)
|
124
|
+
Base64.strict_decode64(token)
|
125
|
+
end
|
126
|
+
|
127
|
+
def xor_byte_strings(s1, s2)
|
128
|
+
s1.bytes.zip(s2.bytes).map { |(c1,c2)| c1 ^ c2 }.pack('c*')
|
128
129
|
end
|
129
130
|
end
|
130
131
|
end
|
@@ -13,7 +13,7 @@ module Rack
|
|
13
13
|
# This middleware is not used when using the Rack::Protection collection,
|
14
14
|
# since it might be a security issue, depending on your application
|
15
15
|
#
|
16
|
-
# Compatible with
|
16
|
+
# Compatible with rack-csrf.
|
17
17
|
class FormToken < AuthenticityToken
|
18
18
|
def accepts?(env)
|
19
19
|
env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest" or super
|
@@ -10,7 +10,7 @@ module Rack
|
|
10
10
|
# Only accepts unsafe HTTP requests if a given access token matches the token
|
11
11
|
# included in the session *or* the request comes from the same origin.
|
12
12
|
#
|
13
|
-
# Compatible with
|
13
|
+
# Compatible with rack-csrf.
|
14
14
|
class RemoteToken < AuthenticityToken
|
15
15
|
default_reaction :deny
|
16
16
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-protection
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.0.
|
4
|
+
version: 2.0.0.rc1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Konstantin Haase
|
@@ -41,7 +41,7 @@ authors:
|
|
41
41
|
autorequire:
|
42
42
|
bindir: bin
|
43
43
|
cert_chain: []
|
44
|
-
date:
|
44
|
+
date: 2017-03-04 00:00:00.000000000 Z
|
45
45
|
dependencies:
|
46
46
|
- !ruby/object:Gem::Dependency
|
47
47
|
name: rack
|
@@ -141,4 +141,3 @@ specification_version: 4
|
|
141
141
|
summary: Protect against typical web attacks, works with all Rack apps, including
|
142
142
|
Rails.
|
143
143
|
test_files: []
|
144
|
-
has_rdoc:
|