Wen

Golangによるパスワードの保存

N views

パスワードの保存方法として、

平文のパスワードを単方向ハッシュにかけて保存するのが一般的です。

ハッシュアルゴリズムにはSHA-256SHA-1MD5等があります。

Golang は crypto パッケージで簡単に実装することが可能です。

以下、 sha256 によるの実装例です。

//import "crypto/sha256"
h := sha256.New()
io.WriteString(h, "my password")
fmt.Printf("% x", h.Sum(nil))

より良い方法

大抵の場合は、暗号化時に使用されたハッシュアルゴリズムが上記のような公開されているものであることが原因で、かつ技術の進歩によりパソコンの計算力も上がり、ハッカーがrainbow tableを使用することで上記の方法でハッシュされたパスワードをクラックすることが(時間的に)難しく無くなってきたのが現状です。

rainbow table :ハッシュ結果(ダイジェスト)のデータベース

直接的な解決方法の一つは、自分でハッシュアルゴリズムをデザインすることです。

しかしながら、優良なハッシュアルゴリズムはとてもデザインが難しいのです。

そのため、実際のアプリケーションでは既存のハッシュアルゴリズムを利用して複数回ハッシュすることが行われます。

しかし単純な複数回ハッシュでは、ハッカーも当然思いつきます。

そこで、管理者自身だけが知っているランダムな文字列をさらに追加して再度ハッシュします。

そのランダム文字列を salt **(ソルト → 塩)と言います。料理に塩***を*ひとふりするだけでグンとおいしさが増すように、ハッシュからパスワードをクラックする難易度もグンと上がります。

//import "crypto/md5"
h := md5.New()
io.WriteString(h, "your password")

pw_md5 :=fmt.Sprintf("%x", h.Sum(nil))

//saltを指定します
salt := "your salt"

//salt1+ユーザ名+salt2+MD5を連結します。
io.WriteString(h, salt)
//io.WriteString(h, "username") // ユーザ名を連結しても良い
//io.WriteString(h, salt2) // saltは複数かけても良い
io.WriteString(h, pw_md5)

result :=fmt.Sprintf("%x", h.Sum(nil))

これで、saltが漏洩していなければ、ハッカーはもし暗号化された文字列を手に入れてもオリジナルのパスワードが何だったのか推測するのはほとんど不可能です。

もっと安全な方法

しかし、並列計算能力の向上によりrainbow tableを作成するだけの十分なリソースがあればこのような攻撃はすでにまったくもって可能です。

そこで、故意にパスワードの計算に必要となるリソースと時間を増加させることによって、誰にもrainbow tableを作成するのに必要となるリソースを与えないという方法があります。

この方法、アルゴリズムの因子(パラメータ)を調整することで計算強度を上げることにより実現できます。

Golang には既にscrypt というライブラリが用意されていて、以下のその例です。

package main

import (
    "encoding/base64"
    "fmt"
    "log"

    "golang.org/x/crypto/scrypt"
)

func main() {
  password = "your password"

    // saltはご自身のものに入れ替えてください
    salt := "your salt"

    dk, err := scrypt.Key([]byte(password), []byte(salt), 1<<15, 8, 1, 32)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(base64.StdEncoding.EncodeToString(dk))
}

scryptの詳細については、こちらに論文があります。

参考記事

Build web application with Golang

php:安全なパスワードハッシュ

本記事は 「表示 - 非営利 - 改変禁止 4.0 国際 (CC BY-NC-ND 4.0)」 を採用。