ʕ ◔ϖ◔ʔ < Go is God.
文字列からハッシュを生成して返す関数
パスワードを生の文字列のまま保存してはならない(いましめ)。
今まではフレームワークが勝手にやってくれてる的な状態に頼ってたんですが、自分でやらないといけない空気を感じたので自分でやらないといけないようです。
最初にささっと書いたのがこれです。
import ( "encoding/hex" "crypto/sha256" ) func toHash(password string) string { converted := sha256.Sum256([]byte(password)) return hex.EncodeToString(converted[:]) }
文字列からsha256ハッシュを求めて、出てきたバイトを文字列に直して返します。
が、一度のハッシュ化だけでは全然脆弱っぽいです。
単方向の暗号化はrainbow table
などといった手法でパパッと破られるっぽいです。二重三重にハッシュ化しても無駄で、ソルトをふりかける(一度ハッシュ化した文字列に、任意の文字列を追加して再度ハッシュ化する)などの対策が必要になるっぽいです。
ここら辺で書くのが面倒くさくなってきました。
ここまで面倒かつ重要なコードであれば既に存在してるはず、しててくれ……! たのむ!
scrypt vs bcrypt
どうやら二つあるっぽい
とりあえず両方のサンプルコードを試す。
package main import ( "encoding/hex" "fmt" "golang.org/x/crypto/bcrypt" "golang.org/x/crypto/scrypt" ) func toHashFromScrypt(pass string) string { salt := []byte("some salt") converted, _ := scrypt.Key([]byte(pass), salt, 16384, 8, 1, 32) return hex.EncodeToString(converted[:]) } func toHashFromBcrypt(pass string) string { converted, _ := bcrypt.GenerateFromPassword([]byte(pass), 10) return hex.EncodeToString(converted[:]) } func main() { fmt.Println(toHashFromScrypt("boee")) fmt.Println(toHashFromBcrypt("boee")) }
みた感じ、bcryptの方が高級なインターフェースを備えてるっぽい?
ただ、外部から明示的にsaltを指定出来ないっぽい。
ここら辺は疎いのでベストな方法はわからないが、とりあえずscryptを使う方針で考えておこうかなーなどとおもいました(対した理由はない)。
なおGitHubにあるbcryptは上記のものとはまた違うものであるもよう。
jameskeane/bcrypt · GitHub
なおbcryptはCompareHashAndPassword
という関数を使ってhashと比較することでタイミング攻撃の脆弱性に対処出来るらしい(参考:http://www.somethingsimilar.com/2012/05/24/finding-go.crypto-and-go.net/)。
ハード系のコードを書く場合はscryptよりもbcryptを使って、さらにbytes.Equal
を使わないようにするべきだろう。
セキュリティは色々覚えることがあって大変だしめんどくさいんですけども平文パスワードをメールで送ってくるようなサービスを作らないように気をつけたいですね。
あれから4年、そこにはいまだに生パスワードを平文メールで送る企業の姿が! - ちょろげ日記
パスワードをハッシュ化する理由の徳丸さんによる解説

体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践
- 作者: 徳丸浩
- 出版社/メーカー: SBクリエイティブ株式会社
- 発売日: 2013/07/26
- メディア: Kindle版
- この商品を含むブログを見る