bcryptを使ってパスワードをハッシュ値に変換する
会員認証アプリケーションを作成する際、たいていの場合は会員認証にIDとパスワードを用いて認証を行います。
教科書ではパスワードは平文で保管してはいけないと記載があり、
ハッシュ値に変換するなどの手法を紹介しています。
そこで今回はNode.jsでハッシュ値に変換するbcryptの使い方を勉強しました。
■はじめに:パスワードはハッシュ値にするだけでいいの?
私が大学生の時の授業ではとりあえずMD5関数を利用してハッシュ値にしておけ!みたいな時代でしたが、
最近はレインボー攻撃というものが登場しているとのことです。
この攻撃では入手したハッシュ値をあらかじめメッセージをハッシュ値に変換しておいたテーブルとぶつけ、
ハッシュ値にする前のメッセージを入手する手法とのことです。
この対策として下記2つの対応方法がありました。
・saltを付与する
ハッシュ値にするメッセージをm、ハッシュ値の関数をh(x)、saltをsとしたときに、
ハッシュ値=h(m||s)として、saltを追加したメッセージをハッシュ化することで、
元のメッセージの解析を難しくする手法とのことです。
saltをどうするべきか(メッセージの前に付与する?後ろに付与する?saltはユーザごとに作成すべき?など)は、
いろいろな方法がありますが、計算機の進化でハッシュ値計算も時間がかからなくなってしまってきているので、
しないよりした方がいいよ!という観点で深く考えすぎないほうがいいかと思います。
「パスワードにはsalt(塩)を振って使いましょう」という文句が覚えやすそうです。
■bcryptを使ってみる
今回の記事の本題です。Node.jsでbcryptを使ってみます。
bcryptのページは下記となり、bcryptを使用するだけでsaltを付与することとストレッチングを行えます。
www.npmjs.com
Nodeのバージョンによって互換性があるので、今回の記事は下記環境で行います。
・環境
OS: Windows 10 home 64bit
Node: v10.15.0
bcrypt: 3.0.3
・bcryptをインストールする
まずはbcryptをインストールします。
npm install bcrypt --save
上記コマンドを実行しても私の環境ではERRが発生インストールできませんでした。
どうやら依存関係が整っていないらしく、
Installation Instructions · kelektiv/node.bcrypt.js Wiki · GitHub
に従い、管理者権限でpowershellを起動し下記コマンドを実行します。
npm install --global --production windows-build-tools
再度bcryptをインストールします。問題なく成功しました。
npm install bcrypt --save
・bcryptを使ってハッシュ値を作成する
サンプルのスクリプトを用いて確認します。
今回は後続ステップでbcryptで生成したハッシュ値を使うので同期メソッドを利用しています。
非同期が推奨のようですが、DBにハッシュ値にしたパスワードを格納する際は、
非同期メソッドでハッシュ値を作成したときにコールバックに記載するなどしないと、
データがなくて登録処理失敗することが予想されます…
/* bcrypt-sample.js */ 'use strict' const bcrypt = require('bcrypt'); const saltRounds = 10; // ストレッチングの回数 const myPlaintextPassword = 's0/\/\P4$$w0rD'; const someOtherPlaintextPassword = 'not_bacon'; // ハッシュ値を生成する var hash = bcrypt.hashSync(myPlaintextPassword, saltRounds); console.log("myPlaintextPassword: " + myPlaintextPassword); console.log("someOtherPlaintextPassword: " + someOtherPlaintextPassword); console.log("hash: " + hash); //ハッシュ値を比較する console.log("compare : '" + myPlaintextPassword + "' with '" + hash + "', result: " + bcrypt.compareSync(myPlaintextPassword, hash)); console.log("compare: '" + someOtherPlaintextPassword + "' with '" + hash + "', result: " + bcrypt.compareSync(someOtherPlaintextPassword, hash));
上記スクリプトを実行します。
$node bcrypt-sample.js myPlaintextPassword: s0//P4$$w0rD someOtherPlaintextPassword: not_bacon hash: $2b$10$YRZKU2MoISXVNNWSNdVLiuQ4H0cnMpIijbAF99v2Wk907nNBEJMpy compare : 's0//P4$$w0rD' with '$2b$10$YRZKU2MoISXVNNWSNdVLiuQ4H0cnMpIijbAF99v2Wk907nNBEJMpy', result: true compare: 'not_bacon' with '$2b$10$YRZKU2MoISXVNNWSNdVLiuQ4H0cnMpIijbAF99v2Wk907nNBEJMpy', result: false
このようにハッシュ値が生成されていることと、比較結果が確認できます。
bcryptで生成されたハッシュ値の文字数は60となり、
'$2b$10$YRZKU2MoISXVNNWSNdVLiuQ4H0cnMpIijbAF99v2Wk907nNBEJMpy'
の見方について先頭から…
$2b$:暗号化のバージョン
$10$:ストレッチングの回数
YRZKU2MoISXVNNWSNdVLiu:salt
Q4H0cnMpIijbAF99v2Wk907nNBEJMpy:ハッシュ値
となります。
毎回実行するたびにsaltが変更になるため、hashが変更になってしまうので…
DBに格納するときや参照するときは念のため気を付けた方がよさそうです。
■終わりに
IDとパスワードでセキュリティを確保しようというのは、難しい時代になってきたと感じました。
Y社は認証技術の黒帯の方もいるぐらいですし、認証技術は日々あれやこれやとユーザを守るために進歩してますね。
自分たちで認証手法を確立する技術力を持っていたらいいですが、それなりのセキュリティを確保したい場合は既存ライブラリを使うのが早そうです。
内容に誤り等ありましたらご指摘お願いします。
以上。