rack-protection 1.5.5 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +13 -0
- data/License +4 -1
- data/README.md +41 -13
- data/Rakefile +29 -5
- data/lib/rack/protection.rb +41 -24
- data/lib/rack/protection/authenticity_token.rb +181 -9
- data/lib/rack/protection/base.rb +3 -22
- data/lib/rack/protection/content_security_policy.rb +79 -0
- data/lib/rack/protection/cookie_tossing.rb +75 -0
- data/lib/rack/protection/escaped_params.rb +2 -0
- data/lib/rack/protection/form_token.rb +1 -1
- data/lib/rack/protection/http_origin.rb +17 -2
- data/lib/rack/protection/json_csrf.rb +26 -4
- data/lib/rack/protection/path_traversal.rb +4 -12
- data/lib/rack/protection/referrer_policy.rb +25 -0
- data/lib/rack/protection/remote_token.rb +1 -1
- data/lib/rack/protection/session_hijacking.rb +1 -1
- data/lib/rack/protection/strict_transport.rb +39 -0
- data/lib/rack/protection/version.rb +1 -12
- data/lib/rack/protection/xss_header.rb +1 -1
- data/rack-protection.gemspec +26 -104
- metadata +21 -82
- data/spec/authenticity_token_spec.rb +0 -48
- data/spec/base_spec.rb +0 -40
- data/spec/escaped_params_spec.rb +0 -43
- data/spec/form_token_spec.rb +0 -33
- data/spec/frame_options_spec.rb +0 -39
- data/spec/http_origin_spec.rb +0 -38
- data/spec/ip_spoofing_spec.rb +0 -35
- data/spec/json_csrf_spec.rb +0 -58
- data/spec/path_traversal_spec.rb +0 -41
- data/spec/protection_spec.rb +0 -105
- data/spec/remote_referrer_spec.rb +0 -31
- data/spec/remote_token_spec.rb +0 -42
- data/spec/session_hijacking_spec.rb +0 -55
- data/spec/spec_helper.rb +0 -163
- data/spec/xss_header_spec.rb +0 -56
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3268bb2b60f8095b38658717f5e267da2e1dfbee57f487baf39a185d3cf9266
|
4
|
+
data.tar.gz: fc40122b95963a81333da038536782d85a9abdc92b823eb5d3044ef3c5c807c4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7b381c903bb99d1e8cfcd00554642aafc2644f4432987be95e094a84b0f020648efb92b4a48e082cda5749045c44d14e5f08d97a1a733bf2b1e850eeaf69d67b
|
7
|
+
data.tar.gz: bfb60cf9484528f0096cd68a6da55b66fa3471407a120caff83ee961b54043f3e4aca45a219273e7e48cc85ce70742ee75dab750609239fb887dfc35dfbcae59
|
data/Gemfile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
gem 'rake'
|
5
|
+
|
6
|
+
rack_version = ENV['rack'].to_s
|
7
|
+
rack_version = nil if rack_version.empty? or rack_version == 'stable'
|
8
|
+
rack_version = {:github => 'rack/rack'} if rack_version == 'master'
|
9
|
+
gem 'rack', rack_version
|
10
|
+
|
11
|
+
gem 'sinatra', path: '..'
|
12
|
+
|
13
|
+
gemspec
|
data/License
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
-
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2011-2017 Konstantin Haase
|
4
|
+
Copyright (c) 2015-2017 Zachary Scott
|
2
5
|
|
3
6
|
Permission is hereby granted, free of charge, to any person obtaining
|
4
7
|
a copy of this software and associated documentation files (the
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# Rack::Protection
|
2
2
|
|
3
3
|
This gem protects against typical web attacks.
|
4
4
|
Should work for all Rack apps, including Rails.
|
@@ -38,43 +38,55 @@ run MyApp
|
|
38
38
|
|
39
39
|
Prevented by:
|
40
40
|
|
41
|
-
* `Rack::Protection::AuthenticityToken` (not included by `use Rack::Protection`)
|
42
|
-
* `Rack::Protection::FormToken` (not included by `use Rack::Protection`)
|
43
|
-
* `Rack::Protection::JsonCsrf`
|
44
|
-
* `Rack::Protection::RemoteReferrer` (not included by `use Rack::Protection`)
|
45
|
-
* `Rack::Protection::RemoteToken`
|
46
|
-
* `Rack::Protection::HttpOrigin`
|
41
|
+
* [`Rack::Protection::AuthenticityToken`][authenticity-token] (not included by `use Rack::Protection`)
|
42
|
+
* [`Rack::Protection::FormToken`][form-token] (not included by `use Rack::Protection`)
|
43
|
+
* [`Rack::Protection::JsonCsrf`][json-csrf]
|
44
|
+
* [`Rack::Protection::RemoteReferrer`][remote-referrer] (not included by `use Rack::Protection`)
|
45
|
+
* [`Rack::Protection::RemoteToken`][remote-token]
|
46
|
+
* [`Rack::Protection::HttpOrigin`][http-origin]
|
47
47
|
|
48
48
|
## Cross Site Scripting
|
49
49
|
|
50
50
|
Prevented by:
|
51
51
|
|
52
|
-
* `Rack::Protection::EscapedParams` (not included by `use Rack::Protection`)
|
53
|
-
* `Rack::Protection::XSSHeader` (Internet Explorer only)
|
52
|
+
* [`Rack::Protection::EscapedParams`][escaped-params] (not included by `use Rack::Protection`)
|
53
|
+
* [`Rack::Protection::XSSHeader`][xss-header] (Internet Explorer and Chrome only)
|
54
|
+
* [`Rack::Protection::ContentSecurityPolicy`][content-security-policy]
|
54
55
|
|
55
56
|
## Clickjacking
|
56
57
|
|
57
58
|
Prevented by:
|
58
59
|
|
59
|
-
* `Rack::Protection::FrameOptions`
|
60
|
+
* [`Rack::Protection::FrameOptions`][frame-options]
|
60
61
|
|
61
62
|
## Directory Traversal
|
62
63
|
|
63
64
|
Prevented by:
|
64
65
|
|
65
|
-
* `Rack::Protection::PathTraversal`
|
66
|
+
* [`Rack::Protection::PathTraversal`][path-traversal]
|
66
67
|
|
67
68
|
## Session Hijacking
|
68
69
|
|
69
70
|
Prevented by:
|
70
71
|
|
71
|
-
* `Rack::Protection::SessionHijacking`
|
72
|
+
* [`Rack::Protection::SessionHijacking`][session-hijacking]
|
73
|
+
|
74
|
+
## Cookie Tossing
|
75
|
+
|
76
|
+
Prevented by:
|
77
|
+
* [`Rack::Protection::CookieTossing`][cookie-tossing] (not included by `use Rack::Protection`)
|
72
78
|
|
73
79
|
## IP Spoofing
|
74
80
|
|
75
81
|
Prevented by:
|
76
82
|
|
77
|
-
* `Rack::Protection::IPSpoofing`
|
83
|
+
* [`Rack::Protection::IPSpoofing`][ip-spoofing]
|
84
|
+
|
85
|
+
## Helps to protect against protocol downgrade attacks and cookie hijacking
|
86
|
+
|
87
|
+
Prevented by:
|
88
|
+
|
89
|
+
* [`Rack::Protection::StrictTransport`][strict-transport] (not included by `use Rack::Protection`)
|
78
90
|
|
79
91
|
# Installation
|
80
92
|
|
@@ -88,3 +100,19 @@ use Rack::Protection, instrumenter: ActiveSupport::Notifications
|
|
88
100
|
```
|
89
101
|
|
90
102
|
The instrumenter is passed a namespace (String) and environment (Hash). The namespace is 'rack.protection' and the attack type can be obtained from the environment key 'rack.protection.attack'.
|
103
|
+
|
104
|
+
[authenticity-token]: http://www.sinatrarb.com/protection/authenticity_token
|
105
|
+
[content-security-policy]: http://www.sinatrarb.com/protection/content_security_policy
|
106
|
+
[cookie-tossing]: http://www.sinatrarb.com/protection/cookie_tossing
|
107
|
+
[escaped-params]: http://www.sinatrarb.com/protection/escaped_params
|
108
|
+
[form-token]: http://www.sinatrarb.com/protection/form_token
|
109
|
+
[frame-options]: http://www.sinatrarb.com/protection/frame_options
|
110
|
+
[http-origin]: http://www.sinatrarb.com/protection/http_origin
|
111
|
+
[ip-spoofing]: http://www.sinatrarb.com/protection/ip_spoofing
|
112
|
+
[json-csrf]: http://www.sinatrarb.com/protection/json_csrf
|
113
|
+
[path-traversal]: http://www.sinatrarb.com/protection/path_traversal
|
114
|
+
[remote-referrer]: http://www.sinatrarb.com/protection/remote_referrer
|
115
|
+
[remote-token]: http://www.sinatrarb.com/protection/remote_token
|
116
|
+
[session-hijacking]: http://www.sinatrarb.com/protection/session_hijacking
|
117
|
+
[strict-transport]: http://www.sinatrarb.com/protection/strict_transport
|
118
|
+
[xss-header]: http://www.sinatrarb.com/protection/xss_header
|
data/Rakefile
CHANGED
@@ -11,6 +11,33 @@ end
|
|
11
11
|
desc "run specs"
|
12
12
|
task(:spec) { ruby '-S rspec spec' }
|
13
13
|
|
14
|
+
namespace :doc do
|
15
|
+
task :readmes do
|
16
|
+
Dir.glob 'lib/rack/protection/*.rb' do |file|
|
17
|
+
excluded_files = %w[lib/rack/protection/base.rb lib/rack/protection/version.rb]
|
18
|
+
next if excluded_files.include?(file)
|
19
|
+
doc = File.read(file)[/^ module Protection(\n)+( #[^\n]*\n)*/m].scan(/^ *#(?!#) ?(.*)\n/).join("\n")
|
20
|
+
file = "doc/#{file[4..-4].tr("/_", "-")}.rdoc"
|
21
|
+
Dir.mkdir "doc" unless File.directory? "doc"
|
22
|
+
puts "writing #{file}"
|
23
|
+
File.open(file, "w") { |f| f << doc }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
task :index do
|
28
|
+
doc = File.read("README.md")
|
29
|
+
file = "doc/rack-protection-readme.md"
|
30
|
+
Dir.mkdir "doc" unless File.directory? "doc"
|
31
|
+
puts "writing #{file}"
|
32
|
+
File.open(file, "w") { |f| f << doc }
|
33
|
+
end
|
34
|
+
|
35
|
+
task :all => [:readmes, :index]
|
36
|
+
end
|
37
|
+
|
38
|
+
desc "generate documentation"
|
39
|
+
task :doc => 'doc:all'
|
40
|
+
|
14
41
|
desc "generate gemspec"
|
15
42
|
task 'rack-protection.gemspec' do
|
16
43
|
require 'rack/protection/version'
|
@@ -19,13 +46,10 @@ task 'rack-protection.gemspec' do
|
|
19
46
|
# fetch data
|
20
47
|
fields = {
|
21
48
|
:authors => `git shortlog -sn`.force_encoding('utf-8').scan(/[^\d\s].*/),
|
22
|
-
:email =>
|
23
|
-
:files =>
|
49
|
+
:email => ["mail@zzak.io", "konstantin.haase@gmail.com"],
|
50
|
+
:files => %w(License README.md Rakefile Gemfile rack-protection.gemspec) + Dir['lib/**/*']
|
24
51
|
}
|
25
52
|
|
26
|
-
# double email :(
|
27
|
-
fields[:email].delete("konstantin.haase@gmail.com")
|
28
|
-
|
29
53
|
# insert data
|
30
54
|
fields.each do |field, values|
|
31
55
|
updated = " s.#{field} = ["
|
data/lib/rack/protection.rb
CHANGED
@@ -3,36 +3,53 @@ require 'rack'
|
|
3
3
|
|
4
4
|
module Rack
|
5
5
|
module Protection
|
6
|
-
autoload :AuthenticityToken,
|
7
|
-
autoload :Base,
|
8
|
-
autoload :
|
9
|
-
autoload :
|
10
|
-
autoload :
|
11
|
-
autoload :
|
12
|
-
autoload :
|
13
|
-
autoload :
|
14
|
-
autoload :
|
15
|
-
autoload :
|
16
|
-
autoload :
|
17
|
-
autoload :
|
18
|
-
autoload :
|
6
|
+
autoload :AuthenticityToken, 'rack/protection/authenticity_token'
|
7
|
+
autoload :Base, 'rack/protection/base'
|
8
|
+
autoload :CookieTossing, 'rack/protection/cookie_tossing'
|
9
|
+
autoload :ContentSecurityPolicy, 'rack/protection/content_security_policy'
|
10
|
+
autoload :EscapedParams, 'rack/protection/escaped_params'
|
11
|
+
autoload :FormToken, 'rack/protection/form_token'
|
12
|
+
autoload :FrameOptions, 'rack/protection/frame_options'
|
13
|
+
autoload :HttpOrigin, 'rack/protection/http_origin'
|
14
|
+
autoload :IPSpoofing, 'rack/protection/ip_spoofing'
|
15
|
+
autoload :JsonCsrf, 'rack/protection/json_csrf'
|
16
|
+
autoload :PathTraversal, 'rack/protection/path_traversal'
|
17
|
+
autoload :ReferrerPolicy, 'rack/protection/referrer_policy'
|
18
|
+
autoload :RemoteReferrer, 'rack/protection/remote_referrer'
|
19
|
+
autoload :RemoteToken, 'rack/protection/remote_token'
|
20
|
+
autoload :SessionHijacking, 'rack/protection/session_hijacking'
|
21
|
+
autoload :StrictTransport, 'rack/protection/strict_transport'
|
22
|
+
autoload :XSSHeader, 'rack/protection/xss_header'
|
19
23
|
|
20
24
|
def self.new(app, options = {})
|
21
25
|
# does not include: RemoteReferrer, AuthenticityToken and FormToken
|
22
26
|
except = Array options[:except]
|
23
27
|
use_these = Array options[:use]
|
28
|
+
|
29
|
+
if options.fetch(:without_session, false)
|
30
|
+
except += [:session_hijacking, :remote_token]
|
31
|
+
end
|
32
|
+
|
24
33
|
Rack::Builder.new do
|
25
|
-
|
26
|
-
use ::Rack::Protection::AuthenticityToken,options if use_these.include? :authenticity_token
|
27
|
-
use ::Rack::Protection::
|
28
|
-
use ::Rack::Protection::
|
29
|
-
use ::Rack::Protection::
|
30
|
-
use ::Rack::Protection::
|
31
|
-
use ::Rack::Protection::
|
32
|
-
use ::Rack::Protection::
|
33
|
-
use ::Rack::Protection::
|
34
|
-
|
35
|
-
|
34
|
+
# Off by default, unless added
|
35
|
+
use ::Rack::Protection::AuthenticityToken, options if use_these.include? :authenticity_token
|
36
|
+
use ::Rack::Protection::ContentSecurityPolicy, options if use_these.include? :content_security_policy
|
37
|
+
use ::Rack::Protection::CookieTossing, options if use_these.include? :cookie_tossing
|
38
|
+
use ::Rack::Protection::EscapedParams, options if use_these.include? :escaped_params
|
39
|
+
use ::Rack::Protection::FormToken, options if use_these.include? :form_token
|
40
|
+
use ::Rack::Protection::ReferrerPolicy, options if use_these.include? :referrer_policy
|
41
|
+
use ::Rack::Protection::RemoteReferrer, options if use_these.include? :remote_referrer
|
42
|
+
use ::Rack::Protection::StrictTransport, options if use_these.include? :strict_transport
|
43
|
+
|
44
|
+
# On by default, unless skipped
|
45
|
+
use ::Rack::Protection::FrameOptions, options unless except.include? :frame_options
|
46
|
+
use ::Rack::Protection::HttpOrigin, options unless except.include? :http_origin
|
47
|
+
use ::Rack::Protection::IPSpoofing, options unless except.include? :ip_spoofing
|
48
|
+
use ::Rack::Protection::JsonCsrf, options unless except.include? :json_csrf
|
49
|
+
use ::Rack::Protection::PathTraversal, options unless except.include? :path_traversal
|
50
|
+
use ::Rack::Protection::RemoteToken, options unless except.include? :remote_token
|
51
|
+
use ::Rack::Protection::SessionHijacking, options unless except.include? :session_hijacking
|
52
|
+
use ::Rack::Protection::XSSHeader, options unless except.include? :xss_header
|
36
53
|
run app
|
37
54
|
end.to_app
|
38
55
|
end
|
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'rack/protection'
|
2
|
+
require 'securerandom'
|
3
|
+
require 'base64'
|
2
4
|
|
3
5
|
module Rack
|
4
6
|
module Protection
|
@@ -7,24 +9,194 @@ module Rack
|
|
7
9
|
# Supported browsers:: all
|
8
10
|
# More infos:: http://en.wikipedia.org/wiki/Cross-site_request_forgery
|
9
11
|
#
|
10
|
-
#
|
11
|
-
#
|
12
|
+
# This middleware only accepts requests other than <tt>GET</tt>,
|
13
|
+
# <tt>HEAD</tt>, <tt>OPTIONS</tt>, <tt>TRACE</tt> if their given access
|
14
|
+
# token matches the token included in the session.
|
12
15
|
#
|
13
|
-
#
|
16
|
+
# It checks the <tt>X-CSRF-Token</tt> header and the <tt>POST</tt> form
|
17
|
+
# data.
|
14
18
|
#
|
15
|
-
#
|
19
|
+
# Compatible with the {rack-csrf}[https://rubygems.org/gems/rack_csrf] gem.
|
16
20
|
#
|
17
|
-
#
|
21
|
+
# == Options
|
18
22
|
#
|
23
|
+
# [<tt>:authenticity_param</tt>] the name of the param that should contain
|
24
|
+
# the token on a request. Default value:
|
25
|
+
# <tt>"authenticity_token"</tt>
|
26
|
+
#
|
27
|
+
# == Example: Forms application
|
28
|
+
#
|
29
|
+
# To show what the AuthenticityToken does, this section includes a sample
|
30
|
+
# program which shows two forms. One with, and one without a CSRF token
|
31
|
+
# The one without CSRF token field will get a 403 Forbidden response.
|
32
|
+
#
|
33
|
+
# Install the gem, then run the program:
|
34
|
+
#
|
35
|
+
# gem install 'rack-protection'
|
36
|
+
# ruby server.rb
|
37
|
+
#
|
38
|
+
# Here is <tt>server.rb</tt>:
|
39
|
+
#
|
40
|
+
# require 'rack/protection'
|
41
|
+
#
|
42
|
+
# app = Rack::Builder.app do
|
43
|
+
# use Rack::Session::Cookie, secret: 'secret'
|
44
|
+
# use Rack::Protection::AuthenticityToken
|
45
|
+
#
|
46
|
+
# run -> (env) do
|
47
|
+
# [200, {}, [
|
48
|
+
# <<~EOS
|
49
|
+
# <!DOCTYPE html>
|
50
|
+
# <html lang="en">
|
51
|
+
# <head>
|
52
|
+
# <meta charset="UTF-8" />
|
53
|
+
# <title>rack-protection minimal example</title>
|
54
|
+
# </head>
|
55
|
+
# <body>
|
56
|
+
# <h1>Without Authenticity Token</h1>
|
57
|
+
# <p>This takes you to <tt>Forbidden</tt></p>
|
58
|
+
# <form action="" method="post">
|
59
|
+
# <input type="text" name="foo" />
|
60
|
+
# <input type="submit" />
|
61
|
+
# </form>
|
62
|
+
#
|
63
|
+
# <h1>With Authenticity Token</h1>
|
64
|
+
# <p>This successfully takes you to back to this form.</p>
|
65
|
+
# <form action="" method="post">
|
66
|
+
# <input type="hidden" name="authenticity_token" value="#{Rack::Protection::AuthenticityToken.token(env['rack.session'])}" />
|
67
|
+
# <input type="text" name="foo" />
|
68
|
+
# <input type="submit" />
|
69
|
+
# </form>
|
70
|
+
# </body>
|
71
|
+
# </html>
|
72
|
+
# EOS
|
73
|
+
# ]]
|
74
|
+
# end
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
# Rack::Handler::WEBrick.run app
|
78
|
+
#
|
79
|
+
# == Example: Customize which POST parameter holds the token
|
80
|
+
#
|
81
|
+
# To customize the authenticity parameter for form data, use the
|
82
|
+
# <tt>:authenticity_param</tt> option:
|
83
|
+
# use Rack::Protection::AuthenticityToken, authenticity_param: 'your_token_param_name'
|
19
84
|
class AuthenticityToken < Base
|
20
|
-
|
85
|
+
TOKEN_LENGTH = 32
|
86
|
+
|
87
|
+
default_options :authenticity_param => 'authenticity_token',
|
88
|
+
:allow_if => nil
|
89
|
+
|
90
|
+
def self.token(session)
|
91
|
+
self.new(nil).mask_authenticity_token(session)
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.random_token
|
95
|
+
SecureRandom.base64(TOKEN_LENGTH)
|
96
|
+
end
|
21
97
|
|
22
98
|
def accepts?(env)
|
23
99
|
session = session env
|
24
|
-
|
100
|
+
set_token(session)
|
101
|
+
|
25
102
|
safe?(env) ||
|
26
|
-
|
27
|
-
|
103
|
+
valid_token?(session, env['HTTP_X_CSRF_TOKEN']) ||
|
104
|
+
valid_token?(session, Request.new(env).params[options[:authenticity_param]]) ||
|
105
|
+
( options[:allow_if] && options[:allow_if].call(env) )
|
106
|
+
end
|
107
|
+
|
108
|
+
def mask_authenticity_token(session)
|
109
|
+
token = set_token(session)
|
110
|
+
mask_token(token)
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def set_token(session)
|
116
|
+
session[:csrf] ||= self.class.random_token
|
117
|
+
end
|
118
|
+
|
119
|
+
# Checks the client's masked token to see if it matches the
|
120
|
+
# session token.
|
121
|
+
def valid_token?(session, token)
|
122
|
+
return false if token.nil? || token.empty?
|
123
|
+
|
124
|
+
begin
|
125
|
+
token = decode_token(token)
|
126
|
+
rescue ArgumentError # encoded_masked_token is invalid Base64
|
127
|
+
return false
|
128
|
+
end
|
129
|
+
|
130
|
+
# See if it's actually a masked token or not. We should be able
|
131
|
+
# to handle any unmasked tokens that we've issued without error.
|
132
|
+
|
133
|
+
if unmasked_token?(token)
|
134
|
+
compare_with_real_token token, session
|
135
|
+
|
136
|
+
elsif masked_token?(token)
|
137
|
+
token = unmask_token(token)
|
138
|
+
|
139
|
+
compare_with_real_token token, session
|
140
|
+
|
141
|
+
else
|
142
|
+
false # Token is malformed
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# Creates a masked version of the authenticity token that varies
|
147
|
+
# on each request. The masking is used to mitigate SSL attacks
|
148
|
+
# like BREACH.
|
149
|
+
def mask_token(token)
|
150
|
+
token = decode_token(token)
|
151
|
+
one_time_pad = SecureRandom.random_bytes(token.length)
|
152
|
+
encrypted_token = xor_byte_strings(one_time_pad, token)
|
153
|
+
masked_token = one_time_pad + encrypted_token
|
154
|
+
encode_token(masked_token)
|
155
|
+
end
|
156
|
+
|
157
|
+
# Essentially the inverse of +mask_token+.
|
158
|
+
def unmask_token(masked_token)
|
159
|
+
# Split the token into the one-time pad and the encrypted
|
160
|
+
# value and decrypt it
|
161
|
+
token_length = masked_token.length / 2
|
162
|
+
one_time_pad = masked_token[0...token_length]
|
163
|
+
encrypted_token = masked_token[token_length..-1]
|
164
|
+
xor_byte_strings(one_time_pad, encrypted_token)
|
165
|
+
end
|
166
|
+
|
167
|
+
def unmasked_token?(token)
|
168
|
+
token.length == TOKEN_LENGTH
|
169
|
+
end
|
170
|
+
|
171
|
+
def masked_token?(token)
|
172
|
+
token.length == TOKEN_LENGTH * 2
|
173
|
+
end
|
174
|
+
|
175
|
+
def compare_with_real_token(token, session)
|
176
|
+
secure_compare(token, real_token(session))
|
177
|
+
end
|
178
|
+
|
179
|
+
def real_token(session)
|
180
|
+
decode_token(session[:csrf])
|
181
|
+
end
|
182
|
+
|
183
|
+
def encode_token(token)
|
184
|
+
Base64.strict_encode64(token)
|
185
|
+
end
|
186
|
+
|
187
|
+
def decode_token(token)
|
188
|
+
Base64.strict_decode64(token)
|
189
|
+
end
|
190
|
+
|
191
|
+
def xor_byte_strings(s1, s2)
|
192
|
+
s2 = s2.dup
|
193
|
+
size = s1.bytesize
|
194
|
+
i = 0
|
195
|
+
while i < size
|
196
|
+
s2.setbyte(i, s1.getbyte(i) ^ s2.getbyte(i))
|
197
|
+
i += 1
|
198
|
+
end
|
199
|
+
s2
|
28
200
|
end
|
29
201
|
end
|
30
202
|
end
|