暗号学的に弱い乱数生成

暗号学的に弱い乱数生成

説明

トークン、セッションID、パスワード再設定コードなど、予測できてはならない値に math/rand を使うと、セキュリティに必要な乱数の保証を得られません。アルゴリズムとシードによっては、観測した値や推測したシードから後続の値を予測できるため、セキュリティ用途には crypto/rand が必要です。

想定される影響

  • 予測可能なトークンやセッションIDで、アカウントやセッションを乗っ取られるおそれがあります。
  • 再設定・認証コードを推測され、認証手順を迂回される可能性があります。
  • 予測不可能性が必要な暗号パラメーターを誤って生成すると、機密性や完全性が弱まるおそれがあります。

対処方法

  • セキュリティ用の乱数は crypto/rand で生成し、IVとnonceには使用するアルゴリズムの長さ・一意性の要件も適用してください。
  • math/rand は統計的なサンプリングなど、予測可能性がセキュリティへ影響しない用途に限定してください。
  • 再設定トークンには例のように十分なエントロピーを確保し、有効期限、一度だけの使用、試行回数制限を組み合わせてください。
  • 共通の生成関数を使い、乱数の長さとエンコードをレビューしてください。実際のトークンをログに残さないでください。

例

変更前の時刻によるシード設定は、Seed が有効な旧来の動作についての説明です。Go 1.24以降、パッケージの math/rand.Seed は既定では何も行いません。この変更で math/rand がセキュリティ用APIになるわけではありません。fmt.Println は結果の確認用であり、本番ログへトークンを出力しないでください。

変更前

go
package main

import (
    "fmt"
    mrand "math/rand"
    "time"
)

// math/randで再設定トークンを生成する弱い例
func generateResetTokenWeak() string {
    // 旧来の動作: 推測可能な時刻のシード(Go 1.24以降は既定で無効)
    mrand.Seed(time.Now().UnixNano())

    letters := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
    b := make([]rune, 16) // 16文字のトークン
    for i := range b {
        b[i] = letters[mrand.Intn(len(letters))]
    }
    return string(b)
}

func main() {
    token := generateResetTokenWeak()
    fmt.Println("reset token:", token)
}

変更後

go
package main

import (
    "crypto/rand"
    "encoding/hex"
    "fmt"
)

// crypto/randで再設定トークンを生成
func generateResetTokenSecure() (string, error) {
    // 16バイト(128ビット)の乱数を32桁の16進数にする
    b := make([]byte, 16)

    // crypto/rand.Readで暗号学的に安全な乱数を生成
    if _, err := rand.Read(b); err != nil {
        return "", err
    }

    // バイト列を16進数の文字列で表す
    return hex.EncodeToString(b), nil
}

func main() {
    token, err := generateResetTokenSecure()
    if err != nil {
        panic(err)
    }
    fmt.Println("reset token:", token)
}

解説:

  • 変更前: セキュリティ用ではない乱数APIで再設定トークンを生成します。時刻のシードが適用される環境では、推測できるシードが追加のリスクになります。
  • 変更後: crypto/rand で16バイトの乱数を生成し、32桁の16進数で表します。エンコードによってエントロピーが増えるわけではありません。保存、有効期限、使用時の検証は別途実装する必要があります。

参考資料