-
Notifications
You must be signed in to change notification settings - Fork 0
/
rails_cookie_decryptor.rb
176 lines (144 loc) · 4.44 KB
/
rails_cookie_decryptor.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
APP_TITLE = 'Rails Cookie Decryptor'
VERSION = '1.1.1'
Shoes.setup do
gem 'activesupport', '~> 5.0.0'
end
require 'base64'
require 'json'
require 'cgi'
require 'active_support'
require 'active_support/core_ext/string'
module Decryptor
DEFAULT_MSG = 'Unable to decrypt.'.freeze
AVAILABLE_FORMATS = {
rails_2_or_3: 'Rails 2 or 3',
rails_4_or_5: 'Rails 4 or 5'
}
# Default values for Rails 4 apps
DEFAULT_KEY_ITER_NUM = 1000
DEFAULT_SALT = "encrypted cookie"
DEFAULT_SIGNED_SALT = "signed encrypted cookie"
def self.from(format, cookie_str, key, salt, salt_signed, key_iter_num)
debug "Cookie content: #{cookie_str.inspect}"
if format == AVAILABLE_FORMATS[:rails_2_or_3]
return rails_3(cookie_str)
else
return rails_4(cookie_str, key, salt, salt_signed, key_iter_num)
end
end
private
def self.rails_3(cookie_str)
cookie_str = URI.unescape(cookie_str)
data, _digest = cookie_str.to_s.split('--')
return DEFAULT_MSG unless data.present?
decoded_data = ::Base64.decode64(data.to_s)
begin
result = Marshal.load(decoded_data)
rescue => e
log_error(e)
end
return result || DEFAULT_MSG
end
def self.rails_4(cookie_str, key, salt, salt_signed, key_iter_num)
return DEFAULT_MSG unless cookie_str.present? && key.present?
begin
key_generator = ::ActiveSupport::KeyGenerator.new(key, iterations: key_iter_num.to_s.to_i)
secret = key_generator.generate_key(salt)
sign_secret = key_generator.generate_key(salt_signed)
encryptor = ::ActiveSupport::MessageEncryptor.new(secret, sign_secret)
rescue => e
log_error(e)
end
return DEFAULT_MSG unless encryptor.present?
begin
cookie = CGI.unescape(cookie_str)
result = encryptor.decrypt_and_verify(cookie)
rescue => e
log_error(e)
end
return result || DEFAULT_MSG
end
def self.log_error(e)
debug "#{e.class} -- #{e.message} [#{e.backtrace.first}]"
end
end
class AppLogic
def initialize(shoes_app)
%i{ cookie format output options key salt salt_signed key_iter_num }.each do |ivar|
ivar = "@#{ivar}"
instance_variable_set(ivar, shoes_app.instance_variable_get(ivar))
end
end
def toggle_options
if @format.text == Decryptor::AVAILABLE_FORMATS[:rails_2_or_3]
@options.hide
else
@options.show
end
end
def decrypt_cookie
data = Decryptor.from(@format.text, @cookie.text, @key.text, @salt.text, @salt_signed.text, @key_iter_num.text)
pretty_output = begin
JSON.pretty_generate(data)
rescue JSON::GeneratorError
data
end
@output.text = pretty_output
end
end
Shoes.app(title: APP_TITLE, height: 775) do
stack(margin: 10) do
para("#{APP_TITLE} v#{VERSION}", size: 24, align: "center")
para('Currently supporting Rails 2 through 5.', align: "center")
stack(margin_top: 10) do
para('Enter your cookie data here:')
@cookie = edit_box('', width: 575, height: 150)
end
stack(margin_top: 10) do
para('... and tell me a little bit more:')
@format = list_box(items: Decryptor::AVAILABLE_FORMATS.values)
@options = stack(hidden: true) do
flow do
para('Secret Key Base:', width: 200)
@key = edit_line('', width: 350, height: 50, right: 0, margin_bottom: 10)
end
flow do
para('Salt:', width: 200)
@salt = edit_line(Decryptor::DEFAULT_SALT, width: 350, right: 0)
end
flow() do
para('Signed Salt:', width: 200)
@salt_signed = edit_line(Decryptor::DEFAULT_SIGNED_SALT, width: 350, right: 0)
end
flow do
para('Key Iteration Number:', width: 200)
@key_iter_num = edit_line(Decryptor::DEFAULT_KEY_ITER_NUM, width: 350, right: 0)
end
end
end
stack(margin_top: 10) do
para('... and it will decrypt here:')
@output = edit_box('', width: 575, height: 350, state: 'readonly')
end
app_logic = AppLogic.new(self)
@cookie.change do |_editbox|
app_logic.decrypt_cookie
end
@format.change do |_editbox|
app_logic.toggle_options
app_logic.decrypt_cookie
end
@key.change do |_editline|
app_logic.decrypt_cookie
end
@salt.change do |_editline|
app_logic.decrypt_cookie
end
@salt_signed.change do |_editline|
app_logic.decrypt_cookie
end
@key_iter_num.change do |_editline|
app_logic.decrypt_cookie
end
end
end