午前I問題

多要素認証(MFA)に関する記述として、適切なものはどれか。

ア)多要素認証とは、パスワードとPINコードを組み合わせて認証する方式であり、2つの「知識情報」を使用することで安全性を高めている。

イ)TOTP(Time-based One-Time Password)は、サーバとクライアントが共有する秘密鍵と現在時刻をもとに生成されるワンタイムパスワードであり、一定時間(通常30秒)ごとに更新される。

ウ)FIDOによる認証では、サーバ側に生体情報(指紋・顔)を送信して照合するため、サーバが侵害された場合に生体情報が漏洩するリスクがある。

エ)SMS認証は、SIMスワップ攻撃に対して強固な耐性を持つため、NIST SP 800-63Bでは推奨認証手段として位置づけられている。

午前I の解答・解説を見る

正解:

解説: TOTPはRFC 6238で標準化されたワンタイムパスワード方式です。サーバとクライアントが共有秘密鍵と現在時刻(UNIXタイム÷30の整数部)をHMAC演算してパスワードを生成します。ア)多要素認証は「知識情報」「所持情報」「生体情報」の異なるカテゴリから2つ以上を組み合わせる必要があります。パスワードとPINコードはどちらも知識情報のため多要素認証になりません。ウ)FIDOは生体情報をデバイス内でのみ処理しサーバには公開鍵のみを登録します。エ)SMS認証はSIMスワップ攻撃に弱く、NIST SP 800-63Bでは「制限付き(Restricted)」に位置づけられています。

認証要素カテゴリ
知識情報What you knowパスワード、PIN、秘密の質問
所持情報What you haveスマートカード、TOTP、SMS
生体情報What you are指紋、顔認証、静脈

午前II問題

OAuth 2.0に関する記述として、適切なものはどれか。

ア)OAuth 2.0の認可コードフロー(Authorization Code Flow)では、アクセストークンがブラウザのURLフラグメント(#以降)に含まれて返却されるため、ブラウザ履歴やリファラヘッダを通じてトークンが漏洩するリスクがある。

イ)PKCEを使用する認可コードフローでは、クライアントが事前に生成したcode_verifierのハッシュ値(code_challenge)を認可リクエストに含め、トークンリクエスト時にcode_verifierを送信することで、認可コード横取り攻撃を防止する。

ウ)OAuth 2.0のImplicitフローは、アクセストークンを安全に保管できないパブリッククライアント向けに最新の仕様で推奨されており、SPAやモバイルアプリでの標準的な利用方法である。

エ)OAuth 2.0はユーザ認証のためのプロトコルであり、アクセストークンにはユーザのID・名前・メールアドレスなどの属性情報が必ず含まれるよう仕様で定められている。

午前II の解答・解説を見る

正解:

解説: PKCEはRFC 7636で定義されており、①クライアントがランダムなcode_verifierを生成、②SHA-256でハッシュしcode_challengeを作成、③認可リクエストにcode_challengeを含める、④トークンリクエスト時にcode_verifierを送信、⑤サーバがハッシュして比較・検証、という流れで認可コード横取り攻撃を防止します。ア)URLフラグメントでトークンが返されるのはImplicitフローの説明です。ウ)ImplicitフローはOAuth 2.0の最新BCP(RFC 9700)で非推奨とされており、SPAやモバイルアプリにはPKCE付き認可コードフローが推奨されます。エ)OAuth 2.0は認可のプロトコルであり、ユーザ認証はOpenID Connect(OIDC)が担います。

フロー推奨対象現在の推奨
認可コード+PKCESPA・モバイル・サーバ✅ 推奨
Implicit(旧)SPA❌ 非推奨
クライアントクレデンシャルサーバ間(M2M)✅ 推奨

午後問題

D社はマイクロサービス構成のWebサービスを運営しており、サービス間の認証にJWT(JSON Web Token)を使用している。セキュリティ担当のE氏がコードレビューを行ったところ、以下の実装と設定が発見された。

def verify_token(token):
    header = jwt.get_unverified_header(token)
    algorithm = header.get("alg")          # (A) ヘッダからアルゴリズムを取得
    secret = get_secret_key()
    payload = jwt.decode(token, secret, algorithms=[algorithm])
    return payload
{
  "sub": "user_123",
  "role": "user",
  "exp": 9999999999
}
def get_user_data(user_id, token_payload):
    role = token_payload.get("role")
    if role == "admin":
        return db.get_all_users()
    else:
        return db.get_user(user_id)     # (B) user_idはリクエストパラメータから取得

設問1

(A) における問題点を説明し、攻撃者がどのように悪用できるかを60字以内で述べよ。

設問1の解答・解説を見る

正解例: JWTヘッダのアルゴリズムを攻撃者が指定できるため、alg=noneを設定した無署名トークンを作成し、署名検証を回避して任意のペイロードを注入できる。

解説: algフィールドをトークン自身のヘッダから読み取る実装はAlgorithm Confusion攻撃に脆弱です。代表的な攻撃が「alg:none攻撃」と「RS256→HS256混同攻撃」の2種類です。正しい実装ではサーバ側でアルゴリズムを固定します。

# 正しい実装
payload = jwt.decode(token, secret, algorithms=["HS256"])

採点基準:

  • algをヘッダから取得する問題点の言及(2点)
  • alg=noneまたは署名検証回避への言及(2点)

設問2

exp 値と (B) それぞれの問題点を答え、適切な対策を述べよ。

設問2の解答・解説を見る

正解例(exp): exp: 9999999999 は2286年相当の有効期限であり事実上失効しない。トークンが漏洩した場合、長期間にわたって不正利用される。対策:有効期限を短く設定し(15〜30分)、リフレッシュトークンと組み合わせてトークンローテーションを実施する。

正解例(B): user_idをリクエストパラメータから取得しているため、認証済みユーザが他ユーザのIDを指定して他人のデータを取得できる(IDOR:安全でない直接オブジェクト参照)。対策:user_idはJWTペイロードのsubクレームから取得する。

def get_user_data(token_payload):
    user_id = token_payload.get("sub")   # JWTから取得(改ざん不可)
    role = token_payload.get("role")
    if role == "admin":
        return db.get_all_users()
    else:
        return db.get_user(user_id)

採点基準:

  • expが失効しない問題と漏洩時リスクへの言及(2点)
  • IDORまたは「他ユーザのデータ取得」への言及(2点)
  • 対策に「JWTのsubから取得」が含まれる(2点)

重要キーワード

用語説明
TOTP共有秘密鍵と現在時刻をHMAC演算して生成するワンタイムパスワード(RFC 6238)
FIDO生体情報をデバイス内で処理し、公開鍵のみをサーバに登録するパスワードレス認証規格
PKCE認可コードフローで使用する認可コード横取り攻撃対策(RFC 7636)
Algorithm Confusion攻撃JWTのalgヘッダを攻撃者が操作して署名検証を回避する攻撃
IDOR安全でない直接オブジェクト参照。リクエストパラメータを操作して他ユーザのリソースにアクセスする攻撃
トークンローテーションリフレッシュトークン使用時に新しいトークンを発行し古いものを無効化する仕組み

まとめ

  • 午前I視点: MFAの3要素(知識・所持・生体)を異なるカテゴリから組み合わせることが必須。SMS認証はNIST SP 800-63BでRestrictedに位置づけられる点を押さえる
  • 午前II視点: OAuth 2.0のImplicitフロー廃止とPKCE付き認可コードフローへの移行、OAuth 2.0(認可)とOIDC(認証)の役割分担を整理する
  • 午後視点: JWTの実装脆弱性(Algorithm Confusion・長期有効トークン・IDOR)はコードレビュー問題として頻出。各脆弱性の悪用方法と対策をセットで答えられるようにする