現在弊社では、とあるRails製Webメディアの大規模改修を実施中です。それに伴い、メディアの情報や登録ユーザーの情報を移行する必要がありました。今回取り上げているのは、ユーザー情報、特に暗号化されたパスワードの移行に関してです。
環境
- Ruby 2.3.1
- Rails 5
- MySQL 5.7
既存サービスは独自の認証方式
まず、既存サービスでは特定の認証Gemは使用しておらず、独自の認証方式を採用しているようでした。パスワードの暗号化に関しては、以下のようなロジックとなっていました(実際とは少し違います)。
crypted_password = Digest::SHA1.hexdigest("#{salt}#{password}")
つまり、ユーザー毎に生成されるsaltと、ユーザーが入力したパスワードをつないだ文字列を入力として、SHA1方式でハッシュを生成しているようです。
改修後の新サービスではSorceryを使う
一方で、改修後の新サービスでは認証GemのSorceryが導入されます。Sorceryの標準暗号化方式が、前述した既存サービスでの暗号化方式と一致していればよいのですが、そう都合よくは行きません。
なので、新サービスのSorcery内で使用される暗号化方式を、前述した既存サービスの暗号化方式に合わせる必要があります。
Sorcery設定ファイルの編集
Sorceryには、md5、sha1、sha256、aes256等、各種暗号化方式が元々準備してあります。設定ファイルを編集し、使用する暗号化方式を指定します。
Rails.application.config.sorcery.configure do |config|
~中略
config.encryption_algorithm = :sha1
end
前述したように、既存ではsha1が使われていたので、それに合わせました。
Sorcery内のSHA1暗号化方式を変更
上記の設定だけで済めば良かったのですが、既存サービスのSHA1を介した暗号化と、SorceryデフォルトでのSHA1を介した暗号化では、入力値が微妙に違うみたいです。
具体的には、
# 既存サービス
crypted_password = Digest::SHA1.hexdigest("#{salt}#{password}")
# sorceryのデフォルト
crypted_password = Digest::SHA1.hexdigest("#{password}--#{salt}")
という感じで、Sorceryではユーザーが入力したパスワードとsaltの順番が逆になっており、さらにpasswordとsaltの間にハイフンが二つ入るようです。というわけで、Sorcery内でSHA1暗号化を行っている部分を改造します。
require 'digest/sha1'
module Sorcery
module CryptoProviders
class SHA1
include Common
class << self
def encrypt(*tokens)
tokens = tokens.flatten
digest = tokens.shift
# 下記の*tokensとdigestを入れ替えた
stretches.times { digest = secure_digest([*tokens, digest].join) }
digest
end
def secure_digest(digest)
Digest::SHA1.hexdigest(digest)
end
end
end
end
end
これで、Sorcery内の暗号化方式を、既存サービスのものに一致させることができました。
まとめ
日本語の情報が見つからなったので最初は不安になりましたが、案外あっさり終わりました。今回は、CryptoProviders::SHA1を直接編集しましたが、encryption_algorithmにcustomを指定することで、独自の暗号化方式を定義することも可能なので、そちらを用いたほうがよかったかもしれません。