質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.63%

RubyのString#cryptと同じ暗号化文字列をnode.jsで出力するにはどうすれば良いですか?

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,419

hojo

score 191

Ruby2.3.0p0で動作していた古いサービスがあります。

そのサービスのユーザデータは全てデータベース(MySQL)に保存されており、パスワードがRubyのString#cryptによって暗号化されて保存されていることがわかりました。

プロジェクトファイルのconfigには暗号化に必要なsalt文字列も記載されていたのでString#cryptで利用されている暗号化アルゴリズムとconfigに記載されているsaltを利用すれば異なる環境でも同じ暗号化文字列を出力することができると考えています。

現在、サービスの環境をrubyからnodeに切り替えている最中なのですが、古いサービスと新しいサービスとでパスワードの暗号化アルゴリズムが異なっています。

そのため、ユーザデータを新しい環境(node)に移しただけでは当然ログインすることができませんでした。

この問題を解決したいと考えています。

ちなみに、新しい環境(node)は、まだ開発段階なので現在は特に何も考えずSHA256を利用しています。

そこで皆様に以下の質問があります。

  1. node環境でRubyのString#cryptと同じアルゴリズムの暗号化関数を使うにはどうすれば良いですか?
  2. パスワードの暗号化に使うアルゴリズムはSHA256で問題ないですか?

ここからは作業の履歴になりますが、1に関しまして調査したところDESまたはcrypt(3)というアルゴリズムが使われているのかな?と推測しているのですが、暗号化についてあまり詳しくないために確証が持てていません。

わからないならとにかくやってみよう!と思い、とりあえずDESアルゴリズムが利用できる暗号化モジュールを探してみたところ以下が見つかりました。

node-crypt3
https://github.com/sendanor/node-crypt3

node-ffi
http://stackoverflow.com/questions/22401920/how-to-do-unix-crypt3-in-javascript
https://github.com/node-ffi/node-ffi

crypto
https://nodejs.org/api/crypto.html

あれこれ考えたのですが、最後に掲載させていただいたcryptoはnode標準の暗号化モジュールのため、ベターな選択だと思いましたので、まずはこれで試してうまくいかなければ他の方法を考えようと思いました。

cryptoについていろいろと調べているとcrypto.getCiphers()メソッドを実行することにより利用できるアルゴリズムの一覧を取得できることがわかりましたので実行してみました。すると以下のような結果が返ってきました。

[ 'aes-128-cbc',
  'aes-128-cbc-hmac-sha1',
  'aes-128-cbc-hmac-sha256',
  'aes-128-ccm',
  'aes-128-cfb',
  'aes-128-cfb1',
  'aes-128-cfb8',
  'aes-128-ctr',
  'aes-128-ecb',
  'aes-128-gcm',
  'aes-128-ofb',
  'aes-128-xts',
  'aes-192-cbc',
  'aes-192-ccm',
  'aes-192-cfb',
  'aes-192-cfb1',
  'aes-192-cfb8',
  'aes-192-ctr',
  'aes-192-ecb',
  'aes-192-gcm',
  'aes-192-ofb',
  'aes-256-cbc',
  'aes-256-cbc-hmac-sha1',
  'aes-256-cbc-hmac-sha256',
  'aes-256-ccm',
  'aes-256-cfb',
  'aes-256-cfb1',
  'aes-256-cfb8',
  'aes-256-ctr',
  'aes-256-ecb',
  'aes-256-gcm',
  'aes-256-ofb',
  'aes-256-xts',
  'aes128',
  'aes192',
  'aes256',
  'bf',
  'bf-cbc',
  'bf-cfb',
  'bf-ecb',
  'bf-ofb',
  'blowfish',
  'camellia-128-cbc',
  'camellia-128-cfb',
  'camellia-128-cfb1',
  'camellia-128-cfb8',
  'camellia-128-ecb',
  'camellia-128-ofb',
  'camellia-192-cbc',
  'camellia-192-cfb',
  'camellia-192-cfb1',
  'camellia-192-cfb8',
  'camellia-192-ecb',
  'camellia-192-ofb',
  'camellia-256-cbc',
  'camellia-256-cfb',
  'camellia-256-cfb1',
  'camellia-256-cfb8',
  'camellia-256-ecb',
  'camellia-256-ofb',
  'camellia128',
  'camellia192',
  'camellia256',
  'cast',
  'cast-cbc',
  'cast5-cbc',
  'cast5-cfb',
  'cast5-ecb',
  'cast5-ofb',
  'des',
  'des-cbc',
  'des-cfb',
  'des-cfb1',
  'des-cfb8',
  'des-ecb',
  'des-ede',
  'des-ede-cbc',
  'des-ede-cfb',
  'des-ede-ofb',
  'des-ede3',
  'des-ede3-cbc',
  'des-ede3-cfb',
  'des-ede3-cfb1',
  'des-ede3-cfb8',
  'des-ede3-ofb',
  'des-ofb',
  'des3',
  'desx',
  'desx-cbc',
  'id-aes128-CCM',
  'id-aes128-GCM',
  'id-aes128-wrap',
  'id-aes192-CCM',
  'id-aes192-GCM',
  'id-aes192-wrap',
  'id-aes256-CCM',
  'id-aes256-GCM',
  'id-aes256-wrap',
  'id-smime-alg-CMS3DESwrap',
  'idea',
  ... 19 more items ]

どうやらDESアルゴリズムは種類があるようで、String#cryptがどのアルゴリズムを利用しているのかわかりません。

調べてもDESまたはcrypt(3)以上の情報が見つかりませんでしたので、とりあえず'des'を試してみようと思いました。

実行したコードは以下になります。

var cipher = crypto.createCipher('des', 'salt here');
cipher.update('password here')
// <Buffer 80 05 eb a7 29 9e 83 59>

結果がバッファ(バイナリ?)で出力されてしまったようなのですが、String#cryptの暗号化文字列はutf-8で出力されているようなのでutf-8で出力したいです。

調べてみるとこちらでは以下のようにupdate()メソッドをコールしていましたので同じように実行してみました。

cipher.update('password here', 'utf8', 'hex')
// '390910129cef7a1c'

うまくいったか!?と期待したのですが、どうやらString#cryptとそれに利用されているsaltを使って出力した文字列とは異なるようでした。

そもそも、RubyのString#cryptが出力する文字列は'ha4NAXDFh4S3c'のような文字列であり、上記の出力結果はどうみても16進数の数値文字列になっているように見えます。

引数に'hex'を指定しているので当然だろうということで、引数を削ってみたのですがうまく出力できませんでした。

こういう時はドキュメントをしっかり読むことが大切だと思いましてここら辺を見てみると、どうやらencodingはutf8以外にasciilatin1を指定できるようで、また出力方法もhex以外にlatin1base64が指定できるようでした。

ここまで試したところで少し疲れてしまいまして、パターンが多すぎて混乱しています。そもそもものすごく簡単なことを遠回りに実装しているような気もしてきました。

一体、RubyのString#cryptと同じ暗号化文字列をnode.jsで出力するにはどうすれば良いのでしょうか。

以下の質問事項

  1. node環境でRubyのString#cryptと同じアルゴリズムの暗号化関数を使うにはどうすれば良いですか?
  2. パスワードの暗号化に使うアルゴリズムはSHA256で問題ないですか?

に加えて、何か気になることがございましたら指摘していただけると助かります。

宜しくお願いいたします。m(_ _)m

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

1. node環境でRubyのString#cryptと同じアルゴリズムの暗号化関数を使うにはどうすれば良いですか?

Ruby#cryptはシステムライブラリのcrypt(3)をそのまま利用している形です。これはUNIX/Linuxなどのpasswdに使うのと同じ形式です。node.jsにおいてcrypt(3)と互換性があるライブラリはnode-crypt3になりますので、こちらを使うのが一番早いと思います。ただし、crypt(3)が無い環境(例えばWindows)では使えず、使用できるアルゴリズムはcrypt(3)の実装依存になりますので、注意してください。OSやディストリビューションを変更している場合は確認が必要です。

2. パスワードの暗号化に使うアルゴリズムはSHA256で問題ないですか?

すでに暗号化済みのデータが何であるかはその暗号化データのsalt部分を見るとわかります。"$"から始まってなければDES、"$1$"ならMD5、"$5$"ならSHA-256などと決まりがあります。ただ、それらに対応しているかどうかはcrypt(3)の実装次第ですので、言語よりもOSの環境によります。
新たにパスワードを暗号化するのであれば、bcryptの方がいいと最近は言われています。npmでいくつか配付されています。cryptと同じく、bcryptを使っている場合でも、bcrypt自体のバージョンがあっていれば、言語間で共通で使用することも可能です。(cryptとbcryptを混ぜても、salt部分で区別が付くため、保存するカラムを新たに作る必要はありません。ただし、データの長さはかなり長くなるため、DB上のカラムに設定されたサイズに注意が必要です)

いずれにしてもcryptoを直接使用することはあまりお勧めできません。この分野はセキュアな設計が求められるため、専門家では無い人のオレオレ設計では脆弱性が産まれる可能性を大きくしてしまいます。各ライブラリの使用を検討してください。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.63%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る