Bcrypt

bcrypt #

パスワードをシステムに保存する際には、生のパスワード文字列をそのまま保存するのではなく、ハッシュにした文字列で保存するようにします。

bcryptはパスワードに使うことができるハッシュです。

bcryptの結果は次のような文字列となります。

$2b$rounds$salt checksum

最初の$2bはバージョンを表します。$2aか$2bとなります。

次の$roundは10進数2文字の数字で、ラウンド回数(繰り返し計算回数)を表しています。

その次の$マークのあとに、salt 22文字、checksum 31文字と続きます。

合計で、3 + 3 + 1 + 22 + 31 = 60文字となります。

パスワードハッシュの計算 #

ここではGo, Python, Denoでの例を示します。

Go #

golang.org/x/crypto/bcryptを使います。

password_string := "password"

hash, err := bcrypt.GenerateFromPassword([]byte(password_string), bcrypt.DefaultCost)

if err != nil {
    fmt.Println(err)
    return
}

fmt.Println(len(hash), string(hash))

Python #

passlibを使います。

import passlib
import passlib.hash


p = passlib.hash.bcrypt.hash("password")
print(f"{len(p)=}, {p=}")

Deno #

import * as bcrypt from "https://deno.land/x/bcrypt/mod.ts";

const hash = await bcrypt.hash("passowrd");

console.log(hash.length, hash)

パスワードの確認 #

bcryptは同じパスワードを入力したとしても、毎回異なるハッシュとなります。これは、ハッシュの計算のたびにSaltが違う値になるためです。そのため、パスワードを照合する際には、ハッシュを計算したときと同じSaltを使う必要があります。Saltはハッシュに含まれています。

この例では5種類のbcryptハッシュは異なりますが、すべて"password"というパスワードから生成したものです。

Go #

hash := []string{
 "$2a$12$Lt0bQqvq3YwsAnCQr6.oNOaUbKW4AGq1.Gi7k.lQwQnsk6k6jDBSS",
 "$2b$12$m3e0.QF7FCHGUjsePXGUCeWcXGZZP.SBqabv34..SzpDnw31btRdS",
 "$2b$12$5iEjjV6MGrqOOUqC3L5gd.yacxx1/jDo73gfrq1VejeyyHnaPRU9q",
 "$2a$10$Kzbor1QTV5cLwCpXIUhkzem5W/yE5ONAY3fA0tPFShrvzZ9KFKD0K",
 "$2a$10$yNc9FGRhno2h8o8la0YYmeE46M4aEx3G6tLLdGl/xrX8cSvtaKTNa",
}

for _, h := range hash {
 error := bcrypt.CompareHashAndPassword([]byte(h), []byte("password"))
 fmt.Println(error)
}

Python #

hash = [
    '$2a$12$Lt0bQqvq3YwsAnCQr6.oNOaUbKW4AGq1.Gi7k.lQwQnsk6k6jDBSS',
    '$2b$12$m3e0.QF7FCHGUjsePXGUCeWcXGZZP.SBqabv34..SzpDnw31btRdS',
    '$2b$12$5iEjjV6MGrqOOUqC3L5gd.yacxx1/jDo73gfrq1VejeyyHnaPRU9q',
    '$2a$10$Kzbor1QTV5cLwCpXIUhkzem5W/yE5ONAY3fA0tPFShrvzZ9KFKD0K',
    '$2a$10$yNc9FGRhno2h8o8la0YYmeE46M4aEx3G6tLLdGl/xrX8cSvtaKTNa',
]

for h in hash:
    print(f'{passlib.hash.bcrypt.verify("password", h)=}')

Deno #

import * as bcrypt from "https://deno.land/x/bcrypt/mod.ts";

const hash = [
    "$2a$12$Lt0bQqvq3YwsAnCQr6.oNOaUbKW4AGq1.Gi7k.lQwQnsk6k6jDBSS",
    "$2b$12$m3e0.QF7FCHGUjsePXGUCeWcXGZZP.SBqabv34..SzpDnw31btRdS",
    "$2b$12$5iEjjV6MGrqOOUqC3L5gd.yacxx1/jDo73gfrq1VejeyyHnaPRU9q",
    "$2a$10$Kzbor1QTV5cLwCpXIUhkzem5W/yE5ONAY3fA0tPFShrvzZ9KFKD0K",
    "$2a$10$yNc9FGRhno2h8o8la0YYmeE46M4aEx3G6tLLdGl/xrX8cSvtaKTNa",
];

for (const h of hash) {
    const result = await bcrypt.compare("password", h);
    console.log(result)
}