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?

OpenID Connectにおけるnonceパラメーターについて

Last updated at Posted at 2025-08-11

OpenID Connectにおけるnonceパラメーターについて

SSO(Single Sign On)の実装を検討していた際に、nonce について整理したメモです。

nonceとは?

セキュリティの重要な概念の1つとして、nonce(ノンス) があります。
nonce(number used once)は「一度だけ使用される番号」で、重複や再利用を避けるために用いられます。

利用例:

  • 暗号の初期ベクトル(IV)
  • 認証トークン
  • ワンタイムトークン(文脈によってはこう呼ばれる)

OpenID Connect(OIDC)におけるnonceの役割

OIDCにおけるnonceは、リプレイアタック防止のために使われます。

1. リクエスト時の生成

クライアント(例:Webアプリケーション)は、認証リクエストをIdP(Identity Provider)に送信する際にランダムなnonceを生成します。

import os
nonce = os.urandom(20).hex()

2. セッションへの保持

生成したnonceは、クライアント側のセッションストアや一時的ストレージに保存します。

3. IDトークンへの組み込み

IdPは認証成功時、生成されたnonce値をIDトークンに含めてクライアントへ返します。

4. トークンの検証

クライアントは、受け取ったIDトークン内のnonceとセッションに保存したnonceを比較します。

  • 一致 → トークンは有効

  • 不一致 → トークンは拒否(不正アクセスの可能性)

リプレイアタック防止の仕組み

攻撃者が既に 使用済みのIDトークン を再利用しようとした場合、
nonceが一致しないためアクセスは拒否されます。

さらに、検証後はnonceを削除することで、同じIDトークンを再利用することはできません。
これにより、 IDトークンの使い回し(リプレイアタック) を防止できます。

リプレイアタック防止の仕組み(図解付き解説)

09_48_37.png

  1. 認証リクエストの送信
    クライアントは、認証リクエストを送信する際にランダムなnonce(例: A)を生成し、IdP(Identity Provider)へ送ります。

  2. IDトークンの発行
    IdPは認証に成功すると、受け取ったnonceをIDトークンに含めてクライアントへ返します。
    (例: IDトークン { nonce: A }

  3. 攻撃者による再利用試行
    攻撃者がネットワークなどから盗み取った既に使用されたIDトークンを再利用しようとします。

  4. トークンの検証とエラー
    クライアントは受け取ったIDトークン内のnonceと、自分のセッションに保存していたnonceを比較します。

    • 一致しない場合 → エラーとして処理を拒否
    • すでに使用済みのnonceの場合もセッション上に存在しないためエラー

補足:実装時の注意点

  • nonceは検証後に必ず破棄すること
    再利用を完全に防ぐため、検証が終わったnonceは必ずセッションやストレージから削除します。
  • nonceの生成は十分にランダムにすること
    推測や衝突を防ぐため、暗号論的に安全なランダム生成を行う(例: os.urandom()など)。
  • nonceの保存期間は最小限にすること
    セッション有効期限や認証要求のタイムアウトと合わせて、短期間で破棄できるようにする。

これらを実践することで、OpenID Connectのnonceはリプレイアタック防止の強力な手段となります。

Pythonでのnonce生成サンプル

OpenID Connect(OIDC)のnonceは暗号学的に安全な乱数で生成する必要があります。
以下は、Python標準ライブラリのみを使った例です。

import os
import base64
import secrets

# 1) 推奨:Base64URL(URLやCookieに安全に載せられる形式)
def generate_nonce_b64url(n_bytes: int = 20) -> str:
    # n_bytesは16〜32を目安に。20なら約27文字のBase64URL文字列
    raw = os.urandom(n_bytes)
    return base64.urlsafe_b64encode(raw).rstrip(b"=").decode("ascii")

# 2) 代替:Hex(読みやすいが長くなる)
def generate_nonce_hex(n_bytes: int = 20) -> str:
    return os.urandom(n_bytes).hex()

# 3) 簡易:secrets(内部で安全な乱数生成)
def generate_nonce_token(n_bytes: int = 20) -> str:
    return secrets.token_urlsafe(n_bytes)  # Base64URL

# サンプル出力
print(generate_nonce_b64url())  # 例: 'Q2XqU2eZ2q3d8y8lCqGq2h4s1Lc'
print(generate_nonce_hex())     # 例: 'a3f4b2c1d9e84f5f9e20b5dce8af3123a4f6d8b1'
print(generate_nonce_token())   # 例: 'r7vX4m3oZt8GqPjKk1pE9cU3hVw'

実装時のポイント

  • 暗号学的に安全な乱数生成
    os.urandom() または secrets モジュールを利用(randomモジュールは不可)

  • バイト長の目安
    16〜32バイト程度が推奨(セキュリティと長さのバランスを考慮)

  • 検証後は必ず破棄
    nonceは認証後の検証が終わったらセッションから削除すること

まとめ

  • nonceは「一度だけ使用される番号」
  • OIDCではリプレイアタック防止に活用
  • 認証リクエスト時に生成 → セッション保持 → IDトークンと照合 → 検証後破棄
  • 結果:IDトークンは一度しか使えなくなる

つまり、nonceを使うことで、認証に利用するIDトークンは 一回きり になります。

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?