今回は Node.js でよく使われる bcrypt を使ったパスワード認証についてまとめます。
bcryptとは
bcrypt はパスワードを安全にハッシュ化するためのライブラリです。単純に文字列を変換するだけではなく、自動で salt を生成して組み合わせながらハッシュ化してくれます。
まずnpmからインストールして使用します。
npm install bcrypt
検索すると bcrypt と bcrypt.js の2種類を見かけることがあります。
主な違いとしては:
bcrypt
Node.js向け
内部でC++実装が使われており高速
サーバー側で利用されることが多い
bcrypt.js
JavaScriptのみで実装
ブラウザでも使用可能
bcrypt より少し遅い
bcrypt.genSaltについて
まずは salt を生成してみます。
import bcrypt from "bcrypt";
const hashPassword = async () => {
const salt = await bcrypt.genSalt(10);
console.log(salt);
};
hashPassword();
出力例:$2b$10$jnyvp9OYfpLf/bXIp3CVCO
これは「ハッシュ化前のsalt」です。salt は、同じパスワードでも毎回異なるハッシュ値になるように追加されるランダムな値です。前回の記事でも少し触れましたが、saltを使うことでレインボーテーブル攻撃などへの耐性が上がります。
saltRoundsとは?
bcrypt.genSalt(10)
この 10 が saltRounds です。これは「どれくらい計算コストをかけるか」を意味します。
数字が大きいほど↓
- セキュリティは上がる
- ハッシュ化に時間がかかるという特徴があります。
イメージとしては:
saltRounds 特徴
8 軽め
10 一般的によく使われる
12以上 より安全だが重い
初学習では 10 を使えば問題ありません。内部的には、rounds が1増えるごとに計算量が約2倍になります。
パスワードをハッシュ化する
次に、実際にパスワードをハッシュ化します。
import bcrypt from "bcrypt";
const hashPassword = async (pw) => {
const salt = await bcrypt.genSalt(10);
const hash = await bcrypt.hash(pw, salt);
console.log(salt);
console.log(hash);
};
hashPassword("ari09A");
出力例:$2b$10$a1u/gFwo2f.7bFK/rJLne.
$2b$10$a1u/gFwo2f.7bFK/rJLne.nGnfcF5uJYRxu.fMuwALh0elinDT54y
実は、hash の前半部分には salt の情報も含まれています。
つまり bcrypt は:
salt、ハッシュ値を1つの文字列として保存しています。
そのため、別途 salt をデータベースで管理する必要はありません。
また、salt は毎回ランダム生成されるため、同じパスワードでも毎回異なるハッシュ値になります。
bcrypt.compareについて
では、ログイン時はどう比較しているのでしょうか?
そこで使うのが bcrypt.compare() です。
bcrypt.compare(myPlaintextPassword, hash, function(err, result) {
// result == true
});
一致すれば true、違えば false を返します。
async/await で書くと:
import bcrypt from "bcrypt";
const login = async (pw, hashedPw) => {
const result = await bcrypt.compare(pw, hashedPw);
// hashedPw は本来DBに保存されている
if (result) {
console.log("login is successful!!");
} else {
console.log("login is failed");
}
};
login(
"ari09A",
"$2b$10$a1u/gFwo2f.7bFK/rJLne.nGnfcF5uJYRxu.fMuwALh0elinDT54y"
);
この場合、元のパスワードと一致しているため true になります。
実はもっと簡単に書けるとbcrypt.hash() の第二引数には、saltそのものではなく rounds を直接渡せます。
const hashPassword = async (pw) => {
const hash = await bcrypt.hash(pw, 10);
console.log(hash);
};
この場合:
- salt生成
- ハッシュ化を bcrypt が内部でまとめて行ってくれます。
まとめ
bcryptを使うことで↓
パスワードを安全にハッシュ化できる
saltを自動生成できる
ログイン時に安全に比較できる
という仕組みを簡単に実装できます。
重要なのは:
ユーザーの生パスワードをDB保存しない
ハッシュ化された値だけ保存する
という点です。
ログイン時には:
- ユーザー入力のパスワード
- DBに保存されているハッシュ値を bcrypt.compare() で比較します。
bcrypt は hash の中に含まれる salt 情報を読み取れるため、開発者が別途 salt を管理する必要はありません。