情報処理安全確保支援士 令和6年春 午後問1解説 ── JWTのalg=noneとAPI認可、WAFの暫定対策

· 更新日: · · 情報処理安全確保支援士, 登録セキスペ, API, APIセキュリティ, JWT, 認証, 認可, WAF, Log4Shell, 情報セキュリティ, 脆弱性, IPA

更新履歴(2件・最終更新 2026年09月07日)

この記事に加えた変更の記録です。アーカイブした更新前のバージョンは、DOI付きの固定URLから読めます。

情報処理安全確保支援士 令和6年春 午後問1の解説を、解答の見取り図、問題文の根拠、実務上の補足の順に整理した。認証・JWT検証・二種類の認可・試行制御・WAFの役割を分け、元の解答例、コード例、チェックリスト、出典と知識マップを維持した。図と章参照を整理し、P呼出し処理と共通層の設計、検証命令の実行確認を明確にした。 更新前のバージョンを見る (DOI: 10.5281/zenodo.22054241)
記事の冒頭に「この記事の知識マップ」節を追加しました。本文で扱う概念とその関係を、要約・図・詳細ページへのリンクにまとめました。本文の主張は変えていません。
初版公開
この記事を引用する(DOI: 10.5281/zenodo.22054240)

この記事はZenodoにアーカイブされています。常に最新版へ解決されるDOIと、いま表示している版に固定されたDOIの両方を下に示します。

小村 豪(2026)「情報処理安全確保支援士 令和6年春 午後問1解説 ── JWTのalg=noneとAPI認可、WAFの暫定対策」合同会社小村ソフト. https://doi.org/10.5281/zenodo.22054240 https://comcomponent.com/blog/sc-exam-r6s-pm-q1-api-security/

DOI(最新版)
10.5281/zenodo.22054240
DOI(この版)
10.5281/zenodo.22586241

「JWTを検証しているのに他人になりすませる」「正しいJWTのまま他人の情報を読める」。この二つは似ていますが、直す場所が違います。前者はトークンの検証、後者は対象データへの認可の問題です。

情報処理安全確保支援士試験 令和6年度春期 午後問1は、スマートフォンから呼び出すAPIを題材に、こうした確認の違いを読み解く問題です。前半ではJWT・利用者ID・更新項目・認証コード、後半ではライブラリの脆弱性とWAFを扱います。1

本記事は、公式の解答例と採点講評を土台に、「解答の要点 → 問題文の根拠 → 実務で追加する設計」の順で説明します。試験の字数内で答える内容と、実システムで必要な対策を混同せずに読み進めてください。23

1. まず結論 ── 検証に成功しても、次の確認は省略できない

この記事を貫く原則は、一つ前の検証に成功したことを、次の信頼境界を省略する理由にしないことです。信頼境界とは、外から来た値を、どの条件で信用してよいかを決める区切りです。

正しいJWTを持っていても、他人の情報を読んでよいとは限りません。自分の情報を更新できても、課金状態まで変更してよいとは限りません。認証コードの有効期限やWAFも、それぞれ別の確認・運用を必要とします。

解答の見取り図

次の表は公式解答の要点です。文章による解答は、各章で設問の字数と根拠を確認してください。2

設問・空欄 解答の要点 詳しい説明
設問1・a ステートレス 3章
設問2(1)・b 500秒 4章
設問2(2) JWTヘッダのalgを対象に、NONEでないことを検証 5章
設問2(3) JWTの利用者IDとmidの一致を検証 6章
設問2(4)・c 共通モジュールP 7章
設問2(5)・d 連続失敗回数がしきい値を超えたらアカウントをロック 8章
設問3(1) テストサーバのindex.htmlへのアクセスを記録・確認 9章
設問3(2)・e、f どちらもHeader 10.1節
設問3(3) 大文字・小文字の両方に対応する正規表現 10.2節
設問3(4) 誤検知による遮断を防ぎ、アラートを精査する 10.3節

まず2章で問題の構成を押さえ、3〜10章を設問順に読むと全体を追えます。解答の書き方は12章、実務の設計・コードレビューに使うチェックリストは13章にまとめています。

図の実線は常に成り立つ関係、破線は条件付きの関係です(成立条件は詳細ページの各関係の説明に記載)。関係すべての一覧(全18件、根拠・確度つき)と主要概念の定義は知識マップ詳細ページにまとめています。データ: JSON-LD / Turtle

2. 問題の構成 ── 五つの確認を分けて読む

問題の舞台は、ヘルスケアサービスを新規に提供しようとしているG社です。利用者はスマートフォンアプリから食事や体重などを入力し、健康リスクの判定や食事メニューの助言を受けます。システムはクラウド上に構築され、APIゲートウェイ、イベント駆動の処理、マネージドデータベースを組み合わせています。

問題文の製品名やサービス名は抽象化されています。本記事では問題冊子の図表をそのまま転載せず、設問を理解するための構造を言い換えて説明します。公式解答の要点を示す箇所と、実務上の補足は区別します。1

問題全体像認証コード、JWT、対象ID、更新項目、外部入力からの実行をそれぞれ別の確認として読む。利用者のリクエスト認証コードの照合JWTの発行と検証利用者API対象midの認可更新statusの認可外部入力をログへ記録脆弱なライブラリJNDIによる外部コード実行

図1: 認証からAPIの操作、ログ処理まで、値を信用する場所は一つではない。

2.1. 認証・トークン検証・二種類の認可を分ける

認証コードで本人を確認し、JWTを発行・検証した後にも、データと項目への認可が残ります。外部入力をログへ出す経路にも、入力を命令として扱わせない境界が必要です。

[利用者ID・パスワード]
          |
          v
[4桁コードの確認] ---- 試行回数制限なし ----> 総当たり
          |
          v
[JWTを発行]
          |
          v
[JWTライブラリ] ------- alg=noneを許可 ------> 利用者IDの改ざん
          |
          v
[利用者API]
    |             |
    |             +-- statusを丸ごと渡す ----> プロパティ単位の認可不備
    |
    +-- midを信用する -----------------------> オブジェクト単位の認可不備

[外部入力をログへ記録]
          |
          v
[脆弱なライブラリ] ---- JNDI/LDAP/HTTP ------> 外部コード実行

ここで最も大切なのは、次の区別です。

確認 問うこと この問題で破られた例
認証 あなたは誰か 4桁コードの総当たり
トークン検証 その身元情報は改ざんされていないか alg=none
オブジェクト単位の認可 その利用者のデータへアクセスしてよいか midの差替え
プロパティ単位の認可 その項目を変更してよいか status=paid
入力から実行への境界 外部入力が命令として解釈されないか JNDI Lookup

一つ前の確認に成功したことは、次の確認を省略してよい理由になりません。 正しいJWTを持つ利用者でも、他人のデータを読んでよいとは限りません。自分のデータを更新できる利用者でも、課金状態まで変更してよいとは限りません。

この段階分けができると、設問ごとの答えが暗記ではなくなります。

認証と認可の違い主体を確認しても、その主体に対象データや項目の操作を許可する確認は別に必要になる。認証とトークン検証誰の要求かを確定そのデータへ到達してよいかその項目を変更してよいか

図2: 認証と認可の違い。認証が先で、認可は別の確認である。

2.2. 設問2は、攻撃者が変えた値で区別する

設問2で混同しやすい論点を、攻撃者が制御した値で整理します。

攻撃 攻撃者が変えた値 信頼してはいけなかった場所 根本対策
JWT改ざん JWTヘッダのalg、ペイロードの利用者ID トークン自身が申告する検証アルゴリズム 許可アルゴリズムをサーバ側で固定する
他人の情報取得 リクエストのmid クライアントが指定した対象ID JWTの主体と照合するか、対象IDをJWTから決める
有償利用者への変更 仕様外のstatus 自動バインドされた全プロパティ 更新可能プロパティを許可リスト化する
4桁コード突破 otpの候補 無制限の認証試行 失敗回数制限、遅延、リスク判定を入れる

「入力値を検証する」で全部をまとめないことが重要です。

  • algは、暗号処理のポリシー
  • midは、オブジェクト単位の認可
  • statusは、プロパティ単位の認可
  • otpは、オンライン推測への耐性

同じHTTPリクエストの中にあっても、守る理由が違います。

3. 設問1 ── ステートレスでも、業務データや失敗回数は持つ

設問1は、RESTful APIの設計原則の一つで、セッション管理を行わない性質を問います。

解答: 空欄aは「ステートレス」です。2

3.1. 根拠 ── 各リクエストだけで処理に必要な情報がそろう

ステートレスとは、サーバが一つ前のリクエストの会話状態を覚えておかなくても、各リクエストだけで処理に必要な情報がそろうことです。この問題では、スマートフォンアプリがリクエストごとにJWTをAuthorizationヘッダへ付けます。サーバはそのJWTを検証し、そのリクエストの利用者を識別します。

3.2. 誤解しやすい点 ── データや失敗回数まで捨てるわけではない

誤解しやすいのは、ステートレスを「サーバは状態を何も持たない」と読むことです。実際には、次の状態は普通に持ちます。

  • 利用者情報や健康データを保存するデータベース
  • 課金状態
  • 認証コードの値、有効期限、失敗回数
  • JWTの署名鍵
  • 失効リストを使う設計なら、その失効情報
  • ログや監査記録

持たないのは、会話を続けるためだけのサーバ側セッション状態を、各API呼出しの前提にしないということです。

また、ステートレスであることは安全性を自動的に高めません。JWTを毎回送れば水平分散しやすくなりますが、JWTの検証を誤れば、誤りも全ノードへ一様に広がります。アーキテクチャ上の性質とセキュリティ上の正しさは別です。

ステートレスと保存する状態会話の履歴に依存せず各リクエストを処理しても、業務データや失敗回数などの状態は保存する。JWT付きリクエストその要求だけで利用者を識別処理を実行して応答利用者情報・課金・失敗回数前の会話への依存は不要

図3: 会話への依存をなくすことと、業務・セキュリティの状態を保存することは両立する。

4. 設問2(1) ── 4桁コードの平均突破時間を計算する

解答: 空欄bは「500」です。単位は秒です。2

4.1. 根拠 ── 候補数・試行速度・有効期間をそろえる

認証APIは、利用者IDとパスワードが合っていれば、メールへ4桁の数字を送ります。その後、利用者IDと4桁コードが一致すればJWTを発行します。コードは生成から10分間有効です。

診断では、1秒に10回の試行が可能でした。平均して何秒で突破できるかを求めます。

計算は「候補数の半分」を使う

4桁の数字は、先頭の0を含めて次の1万通りです。

0000, 0001, 0002, ... , 9999

正解が一様に選ばれ、重複なく順に試す場合、試験では平均試行回数を候補数の半分として計算します。

平均試行回数 = 10,000 ÷ 2 = 5,000回
平均時間     = 5,000 ÷ 10回/秒 = 500秒

この近似で、公式解答の 500秒 になります。厳密に1回目から10,000回目までを平均すると5,000.5回ですが、ここでは試験の解答に合わせます。

最大では1,000秒かかりますが、問題が問うのは平均です。そしてコードの有効期間は600秒なので、平均突破時間の500秒より長い。これが「突破される可能性が高い」と判断された理由です。

4桁認証コードの時間感覚試験の近似では平均500秒であり、重複なしの試行なら有効期間600秒で候補の60パーセントを調べられる。候補は10000通り平均は約5000回毎秒10回なら約500秒有効期間600秒より短い600秒で6000候補を確認

図4: 4桁認証コードの時間感覚。候補数の半分を平均で試すと、有効期間内に当たる。

4.2. 実務の補足 ── 失効時間だけでなく、試行できる回数を見る

認証コードの強さは、桁数だけでも有効期間だけでも決まりません。

有効期間中に試せる回数
= 1秒当たりの試行回数 × 有効期間
= 10 × 600
= 6,000回

重複しない値を順に試すなら、1万通りの60%を有効期間内に確認できます。失効時間を設けていても、試行回数を制限していなければ十分ではありません。

現行のNIST SP 800-63Bは、帯域外認証で使う短期秘密について少なくとも6桁を求め、64bit未満なら試行回数制限を必須にしています。また、電子メールを帯域外認証に使わないよう求めています4。試験では与えられた4桁・メール送信という仕様の中で解答しますが、実務の新規設計ではその前提自体も見直すべきです。

5. 設問2(2) ── JWTの検証方法を攻撃者に選ばせない

解答の要点

設問は、修正後のライブラリQが「どのデータに対して、どのような検証を行うか」を、それぞれ20字以内で問います。

解答例は次のとおりです。

項目 解答の要点
検証対象のデータ JWTヘッダ内のalgに指定された値
検証の内容 NONEでないことを検証する

これが問題文の脆弱性に対する直接の修正です。2 「署名を検証する」だけでは、既に実装されている処理を繰り返す答えになってしまいます。採点講評でも、この種の誤答が指摘されています。3

5.1. 根拠 ── 既にある署名検証の「選び方」が誤っている

問題のJWTは、ヘッダ、ペイロード、署名の三つから成ります。

base64url(header).base64url(payload).base64url(signature)

ヘッダには、署名に使うアルゴリズムとしてRS256が記録されていました。ペイロードには利用者IDや発行時刻、有効期限が入っています。

診断者は、次の二つを変更しました。

  1. ヘッダのalgRS256からNONEへ変更する
  2. ペイロードの利用者IDを別の利用者へ変更する

そのJWTを送ると、検証が成功して他人になりすませました。

JWT alg=none の攻撃の流れ攻撃者が検証方法と利用者IDを変えると、noneを許可したライブラリが改ざんJWTを受け入れてしまう。正しいJWTを取得algをnoneへ変更userを別の利用者へ変更ライブラリが署名検証を省略別の利用者として受け入れる

図5: JWT alg=none 攻撃の流れ。攻撃者が検証アルゴリズムを選んでいる。

noneは仕様にない値ではない

RFC 7519には、署名も暗号化も持たないJWTとしてalgnoneの「Unsecured JWT」が定義されています5。したがって、noneという値が仕様上まったく存在しないわけではありません。

問題は、署名付きJWTだけを受け付けるべきAPIが、攻撃者の指定したnoneを受け入れたことです。

脆弱な処理を概念的に書くと、次のようになります。

1. JWTヘッダを読む
2. ヘッダに書かれたalgを見て、検証方法を選ぶ
3. algがnoneなら署名の検証を行わない
4. ペイロードの利用者IDを信用する

攻撃者が制御する入力から、セキュリティの強さそのものを選ばせています。

5.2. 実務の補足 ── 許可アルゴリズムをサーバ側で固定する

ここは、試験の解答と実務の推奨を分ける必要があります。

RFC 8725は、JWTライブラリが呼出し側に許可アルゴリズムの集合を指定させ、その集合以外を使ってはならないとしています6。つまり、次のような考え方です。

悪い考え方:
  token.header.alg != "none" なら受け入れる

良い考え方:
  serverConfig.allowedAlgorithms に含まれる場合だけ受け入れる
  例: allowedAlgorithms = ["RS256"]

noneだけを拒否しても、別の弱いアルゴリズムや、公開鍵方式と共通鍵方式を取り違えるアルゴリズム混同が残る可能性があります。受け入れる条件を否定形で増やすのではなく、許可する条件を狭く固定するのが原則です。

JWTの検証では、アルゴリズムだけでなく、用途に応じて少なくとも次を確認します。

項目 確認すること
署名 想定した鍵とアルゴリズムで検証できるか
iss 信頼する発行者か
aud このAPI向けに発行されたトークンか
exp 有効期限内か
nbf 利用開始時刻より前ではないか
subまたは利用者ID アプリケーション上の有効な主体か
トークン種別 IDトークンとアクセストークンなどを取り違えていないか

この問題ではペイロードのキー名がuserですが、実務では標準のsubを使うか、独自クレームの意味を明確に定義します。

安全なJWT検証と危険なJWT検証トークンの申告で検証方法を決めず、サーバ側の許可条件を満たした後に署名や各クレームを検証する。いいえはいJWTを受信サーバの許可algか拒否署名と各クレームを検証検証済み主体として扱う危険な処理申告されたnoneで検証を省略

図6: 安全な検証と危険な検証。実務では許可するアルゴリズムを狭く固定する。

5.3. 署名と秘匿を分ける ── base64urlは暗号化ではない

もう一つ、JWTでよく起きる誤解があります。ヘッダとペイロードはbase64urlで表現されますが、これは暗号化ではありません。誰でもデコードして読めます。

署名が保証するのは、正しく検証できた場合に限り、内容が発行後に改ざんされていないことです。秘密にしたい個人情報を、署名付きJWTのペイロードへ入れてよいという意味ではありません。

JWTの署名と秘匿は別base64urlは第三者も読める表現であり、署名の改ざん検知と機密性は別に考える。署名付きJWTヘッダとペイロードを読むbase64urlをデコード内容は秘密にならない署名を正しく検証改ざんされていないか確認

図7: 署名は改ざんの検知に使うもので、読めない形に隠す機能ではない。

6. 設問2(3) ── 正しいJWTと、正しい操作対象は別

解答の要点

表5の下線②は、共通モジュールPの呼出し処理へ追加する処理を40字以内で問います。

解答例は次です。2

JWTに含まれる利用者IDがmidの値と一致するかどうかを検証する処理

設問で指定された追加先は、共通モジュールPそのものではなく、Pを呼び出す「P呼出し処理」です。ここへJWTとmidの一致確認を追加します。1

実務では、P側も含め、データへ到達する共通層で認可を一貫して行うのが狙いです。GETとPUTの両方、今後Pを利用する別のAPIにも同じ認可を効かせやすくなります。各画面やエンドポイントへ比較処理をコピーするだけでは、実装漏れが起こります。

6.1. 根拠 ── JWTの主体と操作対象のmidが別々に渡される

次は、JWT自体を改ざんしない攻撃です。

利用者APIは、GETまたはPUTでmidという利用者IDを受け取ります。共通モジュールPは、そのmidにひも付く利用者情報をデータベースから取得・更新します。

攻撃の構図は単純です。

JWTの利用者ID: user01    ← 正しく署名されたJWT
リクエストのmid: user02  ← 攻撃者が変更

JWTの署名は正しいので、認証は成功します。しかし、APIはmid=user02をそのまま信用し、user02の情報を返します。

これはOWASP API Security Top 10 2023でいう Broken Object Level Authorization(BOLA) に当たる典型です。利用者が指定したオブジェクトIDを使ってデータへアクセスするとき、毎回そのオブジェクトへの認可を確認する必要があります7

BOLAの攻撃正しいJWTの主体とリクエストmidが異なるのに、midだけを使って他人の情報を返してしまう。署名が正しいJWT(user01)利用者API変更したmid(user02)主体との照合をしないuser02のデータを取得・更新

図8: BOLAの攻撃。認証は通るが、認可を確認していない。

6.2. 実務の補足 ── 自分専用APIではmidを受け取らない

自分自身の情報だけを取得・更新するAPIなら、クライアントから利用者IDを受け取る必要がありません。

GET /users/me
Authorization: Bearer <JWT>

サーバ側では、検証済みのJWTから主体を取り出します。

principal = validateJwt(request.authorization)
userId = principal.subject
return repository.getUser(userId)

更新も同じです。

principal = validateJwt(request.authorization)
input = validateProfileUpdate(request.body)
repository.updateProfile(
    userId = principal.subject,
    name = input.name,
    age = input.age
)

比較処理は、書けば守れます。しかし、対象IDを外部から受け取らない設計なら、比較処理を書き忘れる種類のバグ自体を減らせます

管理者が他人の利用者情報を操作する必要があるなら、次のように分けます。

PUT /users/me                  一般利用者用
PUT /admin/users/{userId}      管理者用

管理者用には、別の権限、監査ログ、必要なら再認証を要求します。「一般利用者APIに管理者の場合だけ例外を足す」より、認可ポリシーの境界が見えやすくなります。

BOLAの防ぎ方検証済み主体とmidの一致を確認するか、自分専用APIでは対象を主体から導出して比較漏れを減らす。midを受け取るはいいいえ自分専用API検証済みJWTの主体対象IDの決め方主体と一致するか自分のデータを操作拒否主体から対象IDを導出

図9: BOLAの防ぎ方。midを受け取らないか、JWTの主体と照合する。

6.3. 確認 ── 「誰か」と「何をしてよいか」は別

試験でも実務でも、次の言い方が役立ちます。

  • 認証: 誰であるか
  • 認可: その人が何をしてよいか

JWTの署名検証が成功したのは、「このトークンが表す主体を信用できる」というところまでです。「その主体がuser02を読んでよい」は、別に確認しなければなりません。

7. 設問2(4) ── 更新してよい項目だけを受け取る

解答: 空欄cは「共通モジュールP」です。2

7.1. 根拠 ── 仕様外のstatusが、どこへ渡されたか

利用者APIの仕様には、更新用パラメータとして次のものが定義されています。

mid   利用者ID
name  名前
age   年齢

ところが診断者は、仕様にない次の値を追加しました。

status=paid

すると、無償利用者のステータスが有償利用者へ変わりました。

問題文によれば、サービスLは受け取ったパラメータを検証せず、すべて共通モジュールPへ渡していました。Pはそのままデータベースを更新できる作りでした。

したがって、仕様外の値を受け取り、利用者ステータスまで更新できる送信先が共通モジュールPだと分かります。

Mass Assignment仕様外のstatusまで共通モジュールへ渡して更新すると、利用者が課金状態を変更できてしまう。仕様はmid・name・agestatus=paidを追加全パラメータをPへ渡す内部データへ丸ごと反映課金状態がpaidに変わる

図10: Mass Assignment。仕様外のプロパティが内部オブジェクトに丸ごと反映される。

7.2. BOLAとの違い ── 対象ではなく、その中の項目を守る

前章のmid差替えと、今回のstatus追加は似ていますが、守る粒度が違います。

脆弱性 攻撃者が変えるもの 本来確認すべきこと
mid差替え 対象オブジェクト この利用者がこの利用者レコードへアクセスしてよいか
status追加 オブジェクト内のプロパティ この利用者がこの項目を変更してよいか

OWASP API Security Top 10 2023では、後者を Broken Object Property Level Authorization として扱い、以前のMass Assignmentをこの分類に含めています8

7.3. 実務の補足 ── 更新用の入力型を許可リストにする

脆弱な実装は、概念的には次のようなものです。

entity = repository.find(body.mid)
bindAllProperties(entity, body)
repository.save(entity)

画面にはnameageしか入力欄がなくても、攻撃者はHTTPリクエストを直接作れます。UIにない項目は、セキュリティ境界ではありません。

安全な実装は、更新可能な項目を明示します。

input = parseExactSchema(body, fields = ["name", "age"])

entity = repository.find(authenticatedUserId)
entity.name = input.name
entity.age  = input.age
repository.save(entity)

ここで大切なのは二つです。

  1. 更新用の入力型に、利用者が変更してよい項目だけを置く
  2. 仕様外の未知の項目を無視するのではなく、できればエラーとして拒否する

未知の項目を黙って無視すると、攻撃が失敗したことは隠せますが、クライアントの実装ミスや攻撃の兆候を見落とします。互換性上の理由がなければ、厳密なスキーマで拒否した方が調査しやすくなります。

項目単位の認可更新用入力型をnameとageに限定し、課金状態は検証済みの決済通知から専用経路で更新する。はいいいえプロフィール更新要求nameとageだけか許可した項目のみ更新未知の項目を拒否検証済みの決済通知paymentId照合と重複防止専用経路でstatusを更新

図11: 項目単位の認可。更新可能な項目を許可リストで制限し、課金状態は別経路で変更する。

7.4. 課金状態は、検証済みの決済結果からだけ変える

status=paidは、利用者プロフィールの一部ではありません。決済が成功したというサーバ側の事実から導かれる状態です。

利用者のプロフィール更新
  -> name / ageだけ変更可能

決済サービスからの検証済み通知
  -> paymentIdを照合
  -> 重複処理を防止
  -> statusをpaidへ変更

同じデータベース列に保存していても、変更する権限と経路は別です。内部エンティティをそのまま外部APIの入力型に使うと、この境界が消えます。

7.5. 共通部品には、認可と入力制限まで含める

この問題では、JWT管理ライブラリQと共通モジュールPという二つの共通部品が登場します。

共通部品には大きな利点があります。

  • 一か所を直せば、利用する全APIへ修正を反映できる
  • 認可や検証の実装を各機能へ重複させずに済む
  • テスト対象を集約できる
  • ログや監査の形式を統一できる

一方で、誤りも全体へ広がります。

  • ライブラリQがalg=noneを受け入れると、JWTを使う全APIが危険になる
  • 共通モジュールPが任意のmidstatusを受け入れると、GETとPUTの両方が危険になる
  • 脆弱なライブラリHが基盤で使われると、HTTPヘッダをログに出す複数経路が攻撃面になる

したがって、共通化するべきなのは単なるデータアクセスではありません。セキュリティ不変条件を共通化し、その共通部品を単体で厳しく検証する必要があります。

例えば、Pの契約を次のようにします。

P.getOwnUser(authenticatedSubject)
P.updateOwnProfile(authenticatedSubject, ProfileUpdate{name, age})

次のような低水準APIを、一般の呼出し側へそのまま公開しない方が安全です。

P.getUser(arbitraryMid)
P.updateUser(arbitraryMap)

後者が必要なのは管理処理など限定された経路だけです。低水準の自由度を全APIへ配ると、各呼出し側が毎回正しく使うことを期待する設計になります。

共通部品の契約を狭くする認可と入力型を共通部品の契約に含めると、呼出し側が毎回正しく使うことへの依存を減らせる。一般利用者用API検証済み主体と更新DTO共通部品で認可を統一許可されたデータを操作任意ID・任意Mapの操作管理用など限定経路へ分離共通部品を厳しくテスト

図12: 共通化するのはデータアクセスだけでなく、守るべき認可と入力の条件である。

8. 設問2(5) ── 失敗回数を数え、総当たりを止める

4桁コードの総当たりに対して、表5の空欄dへ入る処理を30字以内で答えます。しきい値は10です。

解答例は次です。2

連続失敗回数がしきい値を超えたらアカウントをロックする処理

8.1. 根拠 ── 失敗回数はセキュリティ判断に必要な状態

ここは、設問1のステートレスと矛盾しません。API呼出しの会話状態をサーバセッションとして持たないことと、セキュリティ判断に必要な失敗回数を永続化することは別です。

試行回数制限の有無失敗回数を保持し、しきい値を超えたらロックすることで無制限のオンライン推測を止める。はいいいえコード照合に失敗アカウントの失敗回数を更新しきい値を超えたかアカウントをロック残りの試行を許す制限がなければ試し続けられる

図13: 回数制限の有無。失敗回数制限により総当たりを実用的に止められる。

8.2. 実務の補足 ── ロックによる締め出しにも備える

アカウント単位の回数制限は必要ですが、攻撃者が他人の利用者IDを知っている場合、しきい値を超えるまでわざと失敗して正規利用者を締め出せます。そこで、実務では次を組み合わせます。

制御 役割
アカウント単位の失敗回数 一つのアカウントへの総当たりを止める
段階的な待ち時間 正規利用者の入力ミスを許容しつつ、攻撃速度を落とす
送信元IP・端末・ASNなどの制御 多数のアカウントへ少数回ずつ試す攻撃を抑える
リスクベース判定 普段と異なる地域・端末・速度を強く制限する
利用者への通知 攻撃や誤操作に気付けるようにする
安全な回復手順 ロックを解除する窓口を攻撃経路にしない

さらに、コードを再送したときに失敗回数を0へ戻してはいけません。攻撃者が再送APIを呼ぶたびに試行枠を復活できるからです。現行NIST SP 800-63Bも、新しい認証秘密を生成しても失敗回数をリセットしないよう求めています4

8.3. 再発行・再利用・送信APIまで一組で守る

問題文では有効期限が中心ですが、実務では次も必要です。

  • 成功したコードは直ちに無効化する
  • 同じコードの再利用を拒否する
  • コードそのものをログへ残さない
  • コード照合の成否から、利用者の存在有無を推測できない応答にする
  • コード送信APIにも回数制限を設ける

短い秘密を使う以上、ランダム生成だけに安全性を任せられません。

認証コード対策候補数と有効期限に加え、試行回数の保持、成功後の無効化、通知と回復手順を組み合わせる。はいいいえコードを発行・再発行失敗回数はリセットしない回数・速度を制限して照合照合に成功したかコードを直ちに無効化失敗記録・遅延・ロック通知と安全な回復手順送信APIも制限・コード非記録

図14: 認証コード対策。桁数と失効時間だけでなく、試行制御と運用を組み合わせる。

9. 設問3(1) ── 検証命令の実行をアクセスログで確かめる

解答の要点

設問3(1)は、テストサーバへ何を実装すれば、コマンドが実行されたと確認できるかを問います。

解答例は次です。

テストサーバのindex.htmlへのアクセスを記録し、確認する仕組み2

9.1. 根拠 ── 最後の検証命令まで届いたことを観測する

サービス開始後、広く使われるオープンソースライブラリHに重大な脆弱性Vが公表されます。問題文の流れは、次のとおりです。

  1. 攻撃者が、JNDI Lookupを含む文字列をHTTPヘッダへ入れて送る
  2. 攻撃対象サーバがその値をログへ出力する
  3. 脆弱なライブラリがJNDI Lookupを評価し、攻撃用LDAPサーバへ問い合わせる
  4. LDAPレスポンスが攻撃用HTTPサーバのURLを返す
  5. 攻撃対象サーバがクラスファイルを取得し、コマンドを実行する

これは、固有名を伏せた Log4Shell(CVE-2021-44228)型の攻撃 と読めます。Apacheの説明でも、攻撃者がログメッセージやパラメータを制御できると、LDAPサーバから読み込んだ任意コードを実行できる脆弱性として説明されています9

Log4Shell型脆弱性の確認フローJNDI参照やクラス取得だけでなく、検証コマンドによるindex.html取得をログで確認する。HTTPヘッダの外部入力対象サーバのログ処理JNDIでLDAPへ問い合わせクラス取得先のURLを受信対象サーバがクラスを取得対象サーバで検証命令を実行テストサーバのindex.html取得そのアクセスを記録・確認

図15: Log4Shell型脆弱性の確認フロー。破壊的な命令ではなく、HTTPアクセスの記録で到達を確認する。

検証命令が起こすのは、無害なHTTPアクセスだけ

G社は、システムに影響を与えない検証コードを実行し、外部から脆弱性Vを悪用できるか確認します。検証コードが行う命令は、テストサーバのindex.htmlを取得するだけです。

Webサーバのアクセスログに、攻撃対象サーバからのGETが残れば、少なくとも次の連鎖が通ったことを確認できます。

外部HTTPリクエスト
  -> ログ処理
  -> JNDI Lookup
  -> LDAP応答
  -> クラス取得
  -> 検証コマンド実行
  -> テストサーバへのHTTPアクセス

9.2. なぜ画面表示ではなく、テストサーバのログなのか

攻撃対象はサーバです。利用者のブラウザ画面に変化が出るとは限りません。また、脆弱性が存在しても、途中の外向き通信がファイアウォールで止まる場合があります。

テストサーバ側でアクセスを記録すれば、攻撃対象サーバから外部へ到達したという観測可能な証拠になります。

9.3. 実務の補足 ── 承認を得て、最小の副作用で確かめる

実務で同種の検証を行うときは、必ず次を守ります。

  • 対象システムの所有者から明示的な承認を得る
  • 本番への影響がない、または許容できる検証方法にする
  • 書込み・削除・設定変更などの破壊的な命令を使わない
  • 検証用ドメインやサーバを自社で管理する
  • 検証時刻、送信元、対象、期待するコールバックを記録する
  • 検証後に一時的なLDAP・HTTPサーバや資格情報を撤去する

「任意コード実行を確認する」ことと、「任意の危険なコードを実行する」ことは同じではありません。目的を満たす最小の副作用にします。

検証は最小の副作用に限定する承認を得て無害な確認方法と観測条件を決め、実行後は検証環境や資格情報を撤去する。所有者の明示的な承認影響と観測条件を決める管理下のサーバでアクセス記録期待した時刻・送信元と照合検証環境・資格情報を撤去書込み・削除・設定変更はしない

図16: 観測したい事実を先に決め、無害な確認と後片付けまでを検証手順に含める。

10. 設問3(2)〜(4) ── WAFの検査場所・パターン・動作を決める

10.1. 設問3(2) ── 検査対象は二つともHeader

サービスNのWAFは、検査対象としてGET、POST、PUT、ANY、Header、COOKIE、Multipartを選べます。

攻撃コードはx-api-versionというHTTPヘッダの値に入ります。したがって、表6の空欄eとfは、どちらも Header です。2

根拠は、攻撃文字列が入っている場所

ここは一般知識より、問題文のデータフローを読む問題です。

攻撃文字列の置き場所:
  x-api-versionヘッダ
          |
          v
WAFの検査対象:
  Header

この問題のANYは、全メソッドのパラメータ値を検査する指定であり、ヘッダを検査するHeaderとは別です。1

GETパラメータでもPOST本文でもありません。WAFの機能一覧を見て「攻撃っぽいからANY」と選ぶのではなく、問題文で攻撃者が値を入れた場所を答えます。

WAFの検査場所を選ぶ攻撃名ではなく、問題文で攻撃文字列が格納された場所からWAFの検査対象を決める。攻撃文字列の格納場所を読むx-api-versionヘッダ検査対象はHeader文字の大小を含むパターンへANYは全メソッドのパラメータ

図17: 攻撃名から推測せず、ヘッダという格納場所を検査対象へ対応付ける。

10.2. 設問3(3) ── 各文字について大文字・小文字の両方を許す

最初の案は、概念的には次のルールでした。

Header  \Wjndi\W  遮断
Header  \Wldap\W  遮断

しかし、jNdIのように大文字・小文字を入れ替えると、単純な小文字だけのパターンを回避できます。

設問3(3)の解答例は、次のいずれかです。2

\W[jJ][nN][dD][iI]\W
\W(j|J)(n|N)(d|D)(i|I)\W

問題冊子ではバックスラッシュが日本語環境の字形で円記号に見える場合がありますが、正規表現としては\Wです。\Wは英数字とアンダースコア以外の文字に一致します。JNDI Lookupの構文では、jndiの前後に${:などの非単語文字が現れるため、それを含めて見ています。

同じ考え方でldap側も大文字・小文字を無視する形へできます。

\W[lL][dD][aA][pP]\W

10.3. 設問3(4) ── 検知は誤遮断を避けるために使う

変更後のWAFルールについて、登録セキスペのZ氏は、本番運用開始後の一定期間は動作を「遮断」ではなく「検知」にするよう助言します。

設問は、検知にする利点と、被害を最小化するために実施すべき内容を、それぞれ25字以内で問います。

解答例は次です。2

項目 解答の要点
利点 誤検知による遮断を防ぐことができる
実施すべき内容 アラートを受信したら攻撃かどうかを精査する

根拠 ── 検知は、アラートを精査する運用と一組

検知モードでは、ルールに一致した通信を通しつつ、ログへ記録してアラートを出します。正常なAPI呼出しの中に偶然jndildapという文字列が入る場合でも、いきなり業務を止めません。

その代わり、運用側には次が必要です。

アラート受信
   |
   v
対象リクエストを確認
   |
   +-- 正常通信 -> ルールを狭める、例外条件を検討
   |
   +-- 攻撃     -> 対象を隔離、ログ保全、影響調査、遮断へ

アラートを誰も見ないなら、検知モードには防御効果がありません。検知は、観測して判断する運用と一組です。

10.4. 実務の補足 ── 観測・調整して遮断へ移す

一般的な導入手順は次です。

  1. 検知モードで実トラフィックに当てる
  2. 誤検知と正検知を分類する
  3. 対象ヘッダ、パス、API、文字境界などを調整する
  4. 正常通信への影響が許容できることを確認する
  5. 遮断モードへ移す
  6. 遮断件数と業務影響を監視する

ただし、これは平時の原則です。脆弱性が重大で、現に悪用され、代替手段がない場合は、誤検知による停止より侵害の被害が大きいと判断して、最初から遮断することもあります。試験の状況では、サービスを今までどおり利用できるか確認するため、まず検知を選んでいます。

WAFの検知から遮断へ検知中のアラートを精査し、誤検知の調整と攻撃への対処を行ったうえで遮断後も監視する。誤検知攻撃検知モードで通信を観測アラートの内容はルールや例外条件を調整隔離・ログ保全・影響調査遮断へ移す正常通信への影響を確認遮断件数と業務影響を監視

図18: 検知から遮断へ。まず観測・調整し、影響が許容できることを確認して遮断へ移す。

11. 実務の脆弱性対応 ── WAFで時間を稼ぎ、更新と調査を進める

問題文では、ライブラリHの公式サイトに修正版も暫定対策もまだなく、クラウド事業者の網羅的なWAFルールも最大72時間かかる状況でした。だからG社は、自分たちで影響を確認し、判明しているパターンだけでも一時的に防ぎます。

試験で問われたのは暫定WAFルールとその運用ですが、WAFは脆弱性そのものを直しません。 影響確認・暫定緩和・根本修正は、待ち合わせずに並行できるところを進めます。実務への補足を含めた対応の全体は次のとおりです。

段階 目的 この問題と実務での対応
影響確認 自社が本当に危険かを判断する 無害なコールバックで外部悪用の可否を確認
暫定緩和 修正までの時間を稼ぐ WAFルール、検知・遮断、外向き通信制限
根本修正 脆弱な原因を除去する 修正版ライブラリへ更新
事後確認 既に悪用されていないか調べる WAF・アプリ・DNS・プロキシなどのログ調査
再発防止 次回の判断を速くする 依存関係の棚卸し、SBOM、更新手順、連絡経路

11.1. 暫定ルールを、完全なLog4Shell対策と考えない

試験は、問題文に示された回避方法へ対応する正規表現を答えるものです。実際の攻撃では、文字列の分割、別のLookup、エンコード、別プロトコルなど、シグネチャだけでは網羅しにくい変形があり得ます。

したがって、実務での位置付けは次です。

  1. 今判明している攻撃パターンをWAFで暫定的に止める
  2. 影響を受けるライブラリが本当に含まれるか調べる
  3. 外向きLDAP・RMI・不要なHTTP通信を制限する
  4. 修正版へ更新する
  5. 更新後もログを確認し、侵害の有無を調査する

WAFは、修正版が出るまでの時間を稼ぐ層です。

WAFの位置づけ検知は通信を通して観測し、遮断と外向き通信制限で緩和しながら根本原因を更新で除去する。重大な脆弱性の公表影響確認と暫定対応検知でログ・アラートを得る遮断・外向き通信制限観測に基づき調整・対処修正版ライブラリへ更新侵害の有無を事後確認

図19: WAFの位置づけ。WAFは修正版が出るまでの時間稼ぎであり、根本対策は更新である。

11.2. 平時の準備 ── 使っているライブラリと配置先を把握する

問題文では、G社がF社へライブラリHを使っているか問い合わせても、詳細な構成分析が必要で回答に時間がかかるとされています。

実務では、重大な脆弱性の公表後に初めてJARファイルを探し始めると、対応が遅れます。少なくとも次を平時から持っておくべきです。

  • 直接依存と推移的依存の一覧
  • 配布物に実際に含まれるコンポーネントとバージョン
  • どのサービス・コンテナ・端末へ展開されているか
  • 依存ライブラリを更新して再ビルド・再配布する手順
  • 緊急変更を承認する連絡経路
  • 外向き通信の許可先と、止めたときの影響
  • ログの保存場所と検索方法

SBOMは目的ではありません。「この脆弱性は、どの実行中システムへ影響するか」を短時間で答えるための索引です。

依存関係を実行中システムへ結び付ける依存コンポーネントと配布先を平時から対応付け、脆弱性公表後の調査と更新判断を速める。直接・推移的依存の一覧配布物の実際のバージョン稼働中サービスと配置先影響対象を短時間で判断承認・再ビルド・再配布通信先とログ保存先も把握

図20: SBOMは収集することが目的ではなく、影響先と更新方法を速く判断するための索引である。

11.3. 事後確認 ── 更新しただけで侵害調査を終えない

脆弱性の公表前後に既に攻撃を受けていた可能性があります。修正版へ更新して今後の悪用を止めても、侵害済みの資格情報や設置されたバックドアまでは消えません。

Log4Shell型なら、少なくとも次の観点を調べます。

  • JNDIやLDAPを示す不審な文字列を含むHTTPリクエスト
  • アプリケーションサーバから外部LDAP・RMI・HTTPへの通信
  • 通常と異なる子プロセスの起動
  • 不審なJAR、class、スクリプト、実行ファイルの作成
  • クラウド資格情報や環境変数へのアクセス
  • 更新前後の認証・権限変更・外部送信

WAFログだけで「攻撃されていない」と断定しないことが重要です。WAFを通らない内部経路や、過去に保存されていないログがあるからです。

更新と侵害調査を分ける修正版への更新で今後の悪用を防ぐことと、既に侵害された可能性を調べることは別に必要になる。修正版へ更新今後の悪用を止める更新前後の記録を調べる通信・プロセス・ファイルを照合資格情報や外部送信を確認過去の侵害は消えない

図21: 原因の修正と、既に起きた侵害の調査を分けて進める。

12. 解答の書き方 ── 仕様との差分を、問題文の用語で答える

この問題は、知識問題というより、仕様と実装の差分を読む問題です。

12.1 表の「仕様」と「実装」を分ける

statusの問題では、API仕様にない値が実装では通っています。

仕様:
  mid / name / age

実装:
  受け取ったパラメータをすべてPへ送る

この差分が見えれば、空欄cが共通モジュールPだと分かります。

12.2 攻撃者が変えた値に線を引く

各攻撃で変えられた値は次です。

  • JWTヘッダのalg
  • JWTペイロードの利用者ID
  • APIパラメータのmid
  • 仕様外のstatus
  • 認証APIのotp
  • HTTPヘッダのx-api-version

設問は、ほぼすべて「その値がどこで検証されるべきか」を聞いています。

12.3 解答は問題文の用語へ戻す

実務では「BOLA」「Mass Assignment」「rate limiting」と呼べます。しかし、設問が求めるのは、問題文の構成に合わせた具体的な処理です。

悪い例:

認可を適切に行う。

良い例:

JWTに含まれる利用者IDがmidの値と一致するか検証する。

悪い例:

ブルートフォース対策をする。

良い例:

連続失敗回数がしきい値を超えたらアカウントをロックする。

抽象名を知っているだけでは、字数内で採点可能な答えになりません。

12.4 WAFは「どこに入ったか」を追う

WAFの検査対象は、攻撃種類から推測するのではなく、攻撃文字列の格納場所から決めます。

x-api-versionヘッダに入れた
        ↓
検査対象はHeader

採点講評では全体の正答率は平均的、設問2(2)と設問3(1)はやや低いとされています。3 設問3(1)の正答率がやや低かったのも、図6の攻撃の流れに合わない答えが多かったためです。攻撃手順を矢印で書き直すだけでも、何を観測すべきかが見えます。

問題文から解答へ戻す仕様と実装の差、変更された値、信頼した処理を追い、問題文の用語で具体的な処理を書く。仕様と実装を並べる変更された値を特定どこで信用したかを追う追加する処理を決める問題文の用語と字数へ戻す

図22: 用語を覚えるだけでなく、値の流れを追って具体的な処理として答える。

13. 実務のAPIレビューで使うチェックリスト

この問題を、実際の設計・コードレビューへ持ち帰るためのチェックリストです。

JWT検証

  • 許可する署名アルゴリズムをサーバ設定で固定している
  • noneや想定外のアルゴリズムを拒否する
  • 署名、issaudexpnbfを用途に応じて検証する
  • IDトークン、アクセストークン、リフレッシュトークンを取り違えない
  • 鍵のローテーションと失効時の手順がある
  • JWTペイロードへ秘匿すべき情報を入れていない

オブジェクト単位の認可

  • リクエスト内のIDを変えたとき、他人のデータへ到達できない
  • 一覧、詳細、更新、削除、ダウンロードのすべてで認可している
  • 認可は画面ではなく、データへ到達する共通層で実施している
  • 自分専用APIでは、対象IDをトークンから導出できないか検討した
  • 管理者用の操作は一般利用者用APIとポリシーを分けている

プロパティ単位の認可

  • 外部入力型とデータベースエンティティを分けている
  • 更新可能な項目を許可リストで列挙している
  • 仕様外のプロパティを拒否または監査している
  • 権限、課金、承認、所有者などの状態を利用者入力から変更できない
  • レスポンスにも不要な機密プロパティを含めていない

認証試行

  • アカウント単位の失敗回数制限がある
  • 段階的遅延や送信元単位の制御がある
  • コード再発行で失敗回数がリセットされない
  • 認証コードは一度だけ使える
  • 認証コードやパスワードをログへ残さない
  • ロック解除・回復手順が別の弱い認証経路になっていない

重大な依存ライブラリ脆弱性

  • 実行中のサービスと依存バージョンを対応付けられる
  • 無害な方法で影響を検証する手順がある
  • WAFや外向き通信制限などの暫定策を適用できる
  • 検知アラートを担当者が確認する運用がある
  • 修正版へ更新する緊急リリース経路がある
  • 更新前に悪用された可能性をログから調査する
レビューは正常系の次を試す正常な要求の成功だけでなく、IDや項目の変更、反復試行を別々に確認して共通層の対策を検証する。正常な要求の成功を確認対象IDだけを変更して確認未知の更新項目を追加して確認認証失敗と再発行を反復拒否・記録・回復を確認それぞれ別の確認として扱う

図23: 正常系の成功を出発点に、別々の境界が本当に拒否できるかを確認する。

14. まとめ ── 次の信頼境界を省略しない

令和6年度 春期 午後 問1は、APIセキュリティの論点を一つずつ分離して読む問題です。

JWTを使っていることは、安全な認証を意味しません。署名アルゴリズムを攻撃者に選ばせれば、利用者IDを書き換えられます。

JWTの署名が正しいことは、正しい認可を意味しません。リクエストのmidを信用すれば、正規利用者が他人の情報へアクセスできます。

自分のオブジェクトを更新できることは、すべてのプロパティを変更してよいことを意味しません。statusのような内部状態を自動バインドすれば、権限や課金状態を書き換えられます。

認証コードに有効期限があることは、総当たりに強いことを意味しません。候補数と試行速度を計算し、失敗回数を制限する必要があります。

WAFにルールを入れることは、脆弱性を修正したことを意味しません。検知と遮断で時間を稼ぎ、影響を確認し、最終的にはライブラリを更新します。

脆弱性と対策の対応表トークンと認証試行、対象と更新項目、外部入力からの実行という境界ごとに対策を用意する。信用する境界を分けるトークンと認証試行対象データと更新項目外部入力からの実行許可algを固定・試行を制限主体を照合・更新DTOを限定暫定緩和とライブラリ更新

図24: 脆弱性と対策対応表。破られた境界ごとに対策を分ける。

この問題を貫く原則は一つです。

一つ前の検証に成功したことを、次の信頼境界を省略する理由にしない。

シリーズの前の記事では、令和5年秋 午後問1の格納型XSSと、令和5年秋 午後問2の来客用Wi-Fiからの情報持出しを解説しています。Webサイト全体の確認観点は、IPA「安全なウェブサイトの作り方」をチェックリストとして使うも参照してください。

最終まとめ前の確認に成功したことを理由に、次の確認や運用上の対策を省略してはならない。別途確認する成功したから省略一つ前の検証に成功次の確認も満たすか境界ごとに判断を積み重ねる未確認の境界が残る日常の設計と検証へ反映

図25: 最終まとめ。信頼境界は段階的に確認し、一つを省略してはならない。

参考リンク

  1. IPA, 令和6年度 春期 情報処理安全確保支援士試験 午後 問題冊子。本記事が扱う問題文です。  2 3 4

  2. IPA, 令和6年度 春期 情報処理安全確保支援士試験 午後 解答例。各設問の公式解答例です。  2 3 4 5 6 7 8 9 10 11 12

  3. IPA, 令和6年度 春期 情報処理安全確保支援士試験 午後 採点講評。正答率と誤答傾向の説明です。  2 3

  4. NIST, SP 800-63B: Authentication and Authenticator Management。短期秘密の桁数、試行回数制限、再発行時の失敗回数、電子メールを帯域外認証に使わないことなどを示しています。  2

  5. RFC Editor, RFC 7519: JSON Web Token (JWT)。Unsecured JWTとalg=noneを含むJWTの仕様です。 

  6. RFC Editor, RFC 8725: JSON Web Token Best Current Practices。許可アルゴリズムの固定、発行者・主体・audienceの検証などを定めるBCPです。 

  7. OWASP, API1:2023 Broken Object Level Authorization。利用者が指定するオブジェクトIDごとに認可を確認する必要性を説明しています。 

  8. OWASP, API3:2023 Broken Object Property Level Authorization。Mass Assignmentを含む、プロパティ単位の認可不備と対策を説明しています。 

  9. Apache Logging Services, Security。CVE-2021-44228の影響、JNDIとLDAPを介したコード実行、修正版を説明しています。 

同じタグを共有する最新の記事です。さらに近い話題で知識を深められます。

このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。

この記事は次のサービスページにつながります。近い入口からご覧ください。

HP制作

会員APIやスマートフォン連携では、JWT検証、オブジェクト単位の認可、更新可能プロパティの制限がそのままWebシステムの安全性に直結するためです。

よくある質問

この記事のテーマについて、相談時によくある質問をまとめています。

JWTの署名検証に成功したのに、なぜ他人になりすませるのですか。
この問題では、JWT管理ライブラリがJWTヘッダのalgを攻撃者の指定どおりに受け入れ、alg=noneのJWTを署名なしで正しいものとして扱っていました。したがって、ペイロードの利用者IDを書き換えても検証に成功します。試験の解答は、検証対象をJWTヘッダのalgとし、その値がNONEでないことを確認する、です。ただし実務ではNONEだけを拒否するのでは不十分です。サーバ側の設定で利用を許可するアルゴリズムをRS256などに固定し、トークンが申告したアルゴリズムをそのまま選択に使わないようにします。さらにissuer、audience、有効期限、subjectなども用途に応じて検証します。
JWT内の利用者IDとリクエストのmidを比較すれば、認可対策として十分ですか。
この問題の解答としては十分です。共通モジュールPで、JWTに含まれる利用者IDとmidが一致するかを検証すれば、他人のmidを指定する攻撃を止められます。ただし自分自身の情報だけを扱うAPIなら、実務ではmidをクライアントから受け取らず、検証済みJWTのsubjectから利用者IDを決める設計の方が安全です。例えばGET /users/meやPUT /users/meとすれば、比較処理の実装漏れそのものを起こしにくくできます。管理者が他人を操作するAPIは別のエンドポイントと認可ポリシーに分けます。
statusを追加する攻撃は、なぜ通常の入力値検証だけでは防げないのですか。
nameの長さやageの範囲を検証しても、そもそも受け取るべきでないstatusを自動バインドして内部オブジェクトへ渡していれば防げないからです。問題は値の形式ではなく、そのプロパティを利用者が変更してよいかというプロパティ単位の認可です。更新用の入力型にはnameとageだけを定義し、未知のプロパティは拒否します。課金状態は決済サービスの成功結果など、サーバが信頼できるイベントだけから変更する必要があります。
4桁の認証コードは10分で失効するのに、なぜ危険なのですか。
0000から9999までの候補は1万通りしかなく、1秒に10回試せると平均5,000回、つまり500秒で当たるからです。有効期間の10分は600秒なので、重複しない候補を順に試せば有効期間内に6,000通りを確認できます。失効時間だけでは総当たりを止められません。候補数、試行速度、試行回数上限を一緒に設計する必要があります。
設問の対策はアカウントロックですが、実務でも即時ロックだけでよいですか。
いいえ。問題の空欄には、連続失敗回数がしきい値を超えたらアカウントをロックする処理、が入りますが、固定的な永久ロックだけでは攻撃者が他人のアカウントを意図的にロックするサービス妨害を起こせます。実務ではアカウント単位の失敗回数、段階的な待ち時間、送信元や端末のリスク判定、通知、回復手順を組み合わせます。新しいコードを発行しても失敗回数をゼロへ戻さないことも重要です。
WAFを遮断ではなく検知にする意味は何ですか。
正常な文字列を誤って攻撃と判定したときも、業務通信を止めずに済むことです。問題の解答では利点が誤検知による遮断を防げること、実施すべき内容がアラートを受信したら攻撃かどうかを精査することです。検知モードは放置するための設定ではありません。ログを確認して誤検知を除き、ルールを調整し、遮断へ移すための観測期間として使います。既知の重大脆弱性が実際に悪用されている緊急時には、可用性リスクと比較して先に遮断する判断もあり得ます。
この問題のライブラリHはLog4jですか。
問題文は製品名を伏せていますが、JNDI Lookup、LDAPサーバ、HTTPサーバからのクラス取得、HTTPヘッダに埋め込まれた文字列、CVSS v3.1の高い基本値という攻撃の流れは、Log4Shellとして知られるCVE-2021-44228を抽象化したものと読むのが自然です。本記事ではその対応関係を説明しますが、試験では固有名を答える必要はありません。与えられた攻撃手順とWAF仕様から解答できます。
この問題から実務に持ち帰るべきことは何ですか。
認証に成功したこと、JWTが改ざんされていないこと、対象オブジェクトへアクセスしてよいこと、対象プロパティを変更してよいことは、すべて別の確認だという点です。加えて、短い認証コードには試行回数制限が必要であり、重大なライブラリ脆弱性では影響確認、暫定防御、根本修正を並行して進めます。共通部品へ認可を集約すること、入力スキーマを許可リストにすること、JWTの検証条件をサーバ側で固定すること、依存ライブラリを把握して更新できる状態にすることが実務上の中心です。

著者プロフィール

記事の著者プロフィールページです。

小村 豪

合同会社小村ソフト 代表

Windows ソフト開発、技術相談、不具合調査を中心に、既存資産が残る案件や原因が見えにくい障害調査に強みがあります。

ブログ一覧に戻る