Skip to content
shunsei.dev
Go back

Go言語でのセッション管理:『徳丸本』に学ぶセッションIDの自作禁止と固定化攻撃対策

Edit page

Go言語でのセッション管理:『徳丸本』に学ぶセッションIDの自作禁止と固定化攻撃対策

Webアプリケーション開発、特に認証周りの実装において「セッションIDをどのように生成・管理するか」はセキュリティの生命線です。

名著『体系的に学ぶ 安全なWebアプリケーションの作り方』(通称:徳丸本)では、 「セッションIDを自作してはならない」 と強く警告されています。

今回はGo言語を例に、なぜ自作がNGなのか、そしてライブラリを使っても残る「セッション固定化攻撃」のリスクと実装策について整理します。

1. なぜセッションIDを自作してはいけないのか

セッションIDには、第三者に推測されないための「十分な桁数」と「ランダム性(エントロピー)」が求められます。

もし開発者が time.Now().Unix() や連番、あるいは脆弱な乱数生成器(Goで言う math/rand)を使ってIDを生成してしまうと、攻撃者に次のIDを予測され、他人のセッションを乗っ取られる(セッションハイジャック)リスクが激増します。

Goでの正解:信頼できるライブラリに任せる

Goの標準ライブラリにはセッション管理機構がないため、暗号論的擬似乱数生成器(crypto/rand)を内部で適切に使用している検証済みのライブラリを使用します。

デファクトスタンダードである gorilla/sessions を使うのが一般的です。

// 悪い例:自前で乱数を作る(予測可能になるリスクが高い)
// func generateSessionID() string { ... }

// 良い例:gorilla/sessionsに任せる
// 内部的にcrypto/randが使われ、予測不可能なIDが生成される
var store = sessions.NewCookieStore([]byte("very-secret-key"))

それでも、ライブラリを使っても防げないケースがあることも紹介します。

2. ライブラリを使っても防げない「セッション固定化攻撃」

「予測不可能なID」を使っていれば安全かというと、そうではありません。 セッション固定化攻撃(Session Fixation) という手法が存在するからです。

安全なウェブサイトの作り方 - 1.4 セッション管理の不備 | 情報セキュリティ | IPA 独立行政法人 情報処理推進機構
情報処理推進機構(IPA)の「安全なウェブサイトの作り方 - 1.4 セッション管理の不備」に関する情報です。
安全なウェブサイトの作り方 - 1.4 セッション管理の不備 | 情報セキュリティ | IPA 独立行政法人 情報処理推進機構 favicon www.ipa.go.jp
安全なウェブサイトの作り方 - 1.4 セッション管理の不備 | 情報セキュリティ | IPA 独立行政法人 情報処理推進機構

詳細な攻撃フローは、上記の情報処理推進機構(IPA)の公式サイトで確認してみてください。

攻撃のメカニズム

  1. 攻撃者がサイトにアクセスし、有効なセッションID(例: SID=123)を取得する。
  2. 攻撃者が被害者に http://example.com/login?SID=123 のような罠リンクを踏ませ、そのIDを強制的にブラウザにセットさせる(※Cookieへの注入手法は様々)。
  3. 被害者がそのID(SID=123)を持ったままログインに成功する。
  4. 攻撃者も SID=123 を知っているため、被害者のアカウントでログイン状態に入れてしまう。

パターン1:攻撃の成立フロー(脆弱性がある場合)

alt text

パターン2:対策後のフロー(IDローテーション)

alt text

これを防ぐための鉄則が、 「認証(ログイン)成功時に、セッションIDをローテーション(再生成)する」 ことです。

3. Goによるセッションローテーションの実装

gorilla/sessions には「1行でローテーションするメソッド」が標準ではないため、 「既存セッションの破棄」→「新規セッションの作成」 を明示的に行う必要があります。

以下は、ログインハンドラ内でセッションIDを刷新する実装例です。

package main

import (
	"net/http"
	"github.com/gorilla/sessions"
)

var store = sessions.NewCookieStore([]byte("secret-key"))

func loginHandler(w http.ResponseWriter, r *http.Request) {
	// --- (1) ユーザー認証処理(ID/Pass確認など) ---
	// if !isAuthenticated { return }

	// --- (2) セッション固定化攻撃対策:IDのローテーション ---
	
	// A. 古いセッションを取得
	oldSession, _ := store.Get(r, "mysession")
	
	// B. 古いセッションを無効化(MaxAgeをマイナスにしてCookie削除を指示)
	oldSession.Options.MaxAge = -1
	oldSession.Save(r, w)

	// C. 新しいセッションを作成
	// store.New() を呼ぶことで強制的に新規セッションインスタンスを作る
	newSession, _ := store.New(r, "mysession")
	
	// --- (3) 新しいセッションの設定 ---
	newSession.Values["user_id"] = "user123" // ユーザー情報をセット
	
	// セキュリティ設定(必須)
	newSession.Options.HttpOnly = true // XSS対策:JSからのアクセス禁止
	newSession.Options.Secure = true   // 盗聴対策:HTTPSのみ許可
	newSession.Options.SameSite = http.SameSiteLaxMode

	// 保存(ここで新しいセッションIDが発行され、Set-Cookieされる)
	newSession.Save(r, w)
}

まとめ

セッション管理を安全に行うためのポイントは以下の3点に集約されます。

フレームワークやライブラリを使っていても、「ローテーション」は開発者が明示的に呼び出さないといけないケースが多いため、実装漏れがないかコードレビューでの重要チェックポイントとなります。


Edit page
Share this post on:

Previous Post
【考察】AI時代の「ニュータイプ」論:なぜ論理より「共感」が最強のスキルになるのか
Next Post
「M4 MacBook AirのDocker環境は「OrbStack」一択だった話