Goでパスワードのハッシュ化

クラッカーの攻撃を受けた場合に備えて、DBに保存するユーザーのパスワードはハッシュ化しておくと安心です。

GoではArgon2やscrypt、bcryptといったアルゴリズムで実装することができるようですが、bcryptを実装してみたので、情報をまとめます。

bcryptoパッケージの使い方

bcryptは golang.org/x/crypto に含まれていて、import文に追加するだけで利用できます。

 import (
    "golang.org/x/crypto/bcrypt"
)

暗号化(ハッシュ化)

下記はプレーンテキストの文字列をハッシュ化する関数です。

# 第一引数に文字列、第二引数は暗号化のコスト

hash,err:=bcrypt.GenerateFromPassword([]byte("plain text"),12)

第二引数は暗号化の強度。4~31までの数字を入れることができます。

12を入れた場合は4096ビット(2^12)の強度で暗号化されます。

bcrypt.GenerateFromPassword() 関数はランダムなソルトも追加してくれるので、レインボーテーブル攻撃にも耐性があるようです。

ソルト

パスワードやパスフレーズなどのデータをハッシュ化する際に、一方向性関数の入力に加えるランダムなデータのこと

レインボーテーブル攻撃

ハッシュから平文を得る手法の一つ

ハッシュ値とプレーンテキストの比較

ハッシュ化した値とプレーンテキストがマッチしているかどうかを確認するには bcrypt.CompareHashAndPassword() 関数を使います。

hash:=[]byte("hashed value")
err:=bcrypt.CompareHashAndPassword(hash,[]byte("plain text"))

ハッシュ値と文字列が一致していた場合は nil が返り、不一致の場合はエラーが返ります。

DBの機能を使ってbcrypt処理を実装するリスク

Postgresなどのデータベースには、Goと同様にパスワードをハッシュ化する機能が備わっています。

しかし次のようなリスクがあるため、DB側ではなくサーバーサイドのプログラミングで処理するのが良いようです。

  • MySQLやPostgresでは文字列の暗号化処理にかかる時間が一定ではないため、サイドチャネル攻撃、タイミング攻撃に対して脆弱性がある。
  • プレーンテキストのパスワードを、サーバーサイドからDBへ送信するため、その間に読み取られるリスクがある。

後者のリスクを突いた事故が2018年に実際に起きています。

GitHub Accidentally Recorded Some Plaintext Passwords in Its Internal Logs

https://www.bleepingcomputer.com/news/security/github-accidentally-recorded-some-plaintext-passwords-in-its-internal-logs/

サイドチャネル攻撃は、暗号化処理の挙動を観察することで、使われている暗号化アルゴリズムや内部のプログラムの情報を推測、取得する手法。

タイミング攻撃は暗号化や復号処理にかかった時間を解析して暗号鍵を推測する手法のこと。

まとめ

セキュリティ関連は難しいですが、どの言語でも応用できる知識となるので、折を見て少しずつ身につけていきたいですね。