Проблема с шифрование/дешифрование строки на Ruby для меня казалась до сегодняшнего дня очень простой задачей. Во всем изветсном любителям и профессионалам по укладке рельсов учебнике «Agile web development Ruby on Rails» описывается как зашифровать строку с помощью соли и функции Digest::SHA1.digest. Но как расшифровать эту строку нигде не описано. Мало того, потратив пол дня в поисках данного решения, я ничего подобного не нашел, кроме одной фразы на каком-то форуме: «это не возможно». Кто читал данную книгу, помнит, что после того как строка зашифрована, её только проверяют с ведённым паролем пользователем, впоследствии зашифрованным таким же способом. Но расшифровать её никто и не пытался. Правильно — ведь это невозможно!
Но что делать, если нужно дешифровать сохранённый пароль? Ситуация простая — моё приложение будет обращаться к API некоторого сервиса, для авторизации которого нужен пароль в незашифрованном виде. Потратив ещё несколько часов на поиски, я нашёл, что это можно сделать с помощью модуля Base64. Итого у меня получился такой вот модуль:
#lfield_encriptor.rb
module FieldEncryptor
module ClassMethods
def safe_fields(*args)
args.each do |a|
define_method(a) do
decrypt_field(a)
end
define_method("#{a}=") do |val|
self.write_attribute(a, val)
encrypt_field(a)
end
end
end
end
def self.included(base)
base.extend(ClassMethods)
end
private
def encrypt_field(field)
self.write_attribute(field, Base64.encode64(self.read_attribute(field)))
end
def decrypt_field(field)
Base64.decode64(self.read_attribute(field))
end
end
Ложим его в lib прокта. Далее его можно заюзать в любой модельке:
class User < ActiveRecord::Base
include FieldEncryptor
safe_fields :password
end
Протестируем, что ж у нас получилось:
andrey@andrey:~/projects/test$ script/console
>> u = User.first
=> #<User id: 1, password: «NHNwdXRuMWs=\n»>
>> u.password = 'test'
=> «test»
>> u.save
=> true
>> u
=> #<User id: 1, password: «dGVzdA==\n»>
>> u.password
=> «test»
Как видно из примера все работает как нужно. В базу сохраняется зашифрованная строка, но обращаясь к атрибуту мы имеем незашифрованную строку.
Данное решение имеет большой недостаток: если знать, что строка зашифрованна данным методом, то дешифровать её проще простого. Вследствие этого, такое решение не надежно и пароль хакеры взломают очень просто.
PS. Шифровать и дешифровать строку можно многими методами. Например, есть гемы encryptor, attr_encrypted и т. п. Но есть проблема с сохранением зашифрованной строки с помощью алгоритмов, используемыми этими плагинами, в базу (в частности mysql у меня не получилось). В такой строке постоянно появляется какой-то символ (чёрный ромб с вопросиком внутри), после которого все символы в базе просто обрезается и при выборке получается уже неполная строка. Как и из-за чего это происходит разобраться у меня не получилось. Так или иначе это тема не для рассуждений в этой статье. Надеюсь, моя статья была кому-то полезна. Спасибо за внимание.
Но что делать, если нужно дешифровать сохранённый пароль? Ситуация простая — моё приложение будет обращаться к API некоторого сервиса, для авторизации которого нужен пароль в незашифрованном виде. Потратив ещё несколько часов на поиски, я нашёл, что это можно сделать с помощью модуля Base64. Итого у меня получился такой вот модуль:
#lfield_encriptor.rb
module FieldEncryptor
module ClassMethods
def safe_fields(*args)
args.each do |a|
define_method(a) do
decrypt_field(a)
end
define_method("#{a}=") do |val|
self.write_attribute(a, val)
encrypt_field(a)
end
end
end
end
def self.included(base)
base.extend(ClassMethods)
end
private
def encrypt_field(field)
self.write_attribute(field, Base64.encode64(self.read_attribute(field)))
end
def decrypt_field(field)
Base64.decode64(self.read_attribute(field))
end
end
Ложим его в lib прокта. Далее его можно заюзать в любой модельке:
class User < ActiveRecord::Base
include FieldEncryptor
safe_fields :password
end
Протестируем, что ж у нас получилось:
andrey@andrey:~/projects/test$ script/console
>> u = User.first
=> #<User id: 1, password: «NHNwdXRuMWs=\n»>
>> u.password = 'test'
=> «test»
>> u.save
=> true
>> u
=> #<User id: 1, password: «dGVzdA==\n»>
>> u.password
=> «test»
Как видно из примера все работает как нужно. В базу сохраняется зашифрованная строка, но обращаясь к атрибуту мы имеем незашифрованную строку.
Данное решение имеет большой недостаток: если знать, что строка зашифрованна данным методом, то дешифровать её проще простого. Вследствие этого, такое решение не надежно и пароль хакеры взломают очень просто.
PS. Шифровать и дешифровать строку можно многими методами. Например, есть гемы encryptor, attr_encrypted и т. п. Но есть проблема с сохранением зашифрованной строки с помощью алгоритмов, используемыми этими плагинами, в базу (в частности mysql у меня не получилось). В такой строке постоянно появляется какой-то символ (чёрный ромб с вопросиком внутри), после которого все символы в базе просто обрезается и при выборке получается уже неполная строка. Как и из-за чего это происходит разобраться у меня не получилось. Так или иначе это тема не для рассуждений в этой статье. Надеюсь, моя статья была кому-то полезна. Спасибо за внимание.