rack-protection 2.0.0.beta2 → 2.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
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:
|