科目B対策へ戻る

会員APIの診断:オブジェクト・機能レベルの認可不備とJWT検証

オリジナルREST APIのBOLA・BFLA・マスアサインメント,JWTのalg none・aud未検証,確認コードの総当たりと試行回数制限目安 35分

Web APIの認可に関する次の記述を読んで,設問に答えよ。

F社は,従業員120名の食品会社であり,ミールキットの定期配送サービス(以下,Mサービスという)を運営している。会員数は約30万人である。会員は,スマートフォン用のアプリケーションプログラム(以下,会員アプリという)又はWebブラウザで動作するシングルページアプリケーション(以下,会員SPAという)から,注文の確認,配送日の変更,会員情報の変更を行う。F社の運用担当者は,社内向けのSPA(以下,運用画面という)から会員の照会などを行う。また,配送を委託しているD社の配送員は,配送委託先向けのポータル(以下,配送ポータルという)から配送先の一覧を参照する。

会員アプリ,会員SPA及び運用画面は,いずれも会員API(https://api.mealbox.example)を呼び出す。配送ポータルは,配送API(https://partner-api.mealbox.example)を呼び出す。会員API及び配送APIは,F社の開発チーム(6名)が開発・運用している。

〔認証とアクセストークン〕

会員,運用担当者及び配送員は,F社の認可サーバ(https://auth.mealbox.example)で認証を受け,JSON Web Token(以下,JWTという)形式のアクセストークンを取得する。アクセストークンの有効期間は15分である。認可サーバは,RSA鍵だけを保有し,全てのアクセストークンをRS256で署名する。署名の検証に用いる公開鍵は,認可サーバのJWKSエンドポイントで公開している。アクセストークンのペイロードには,利用者の識別子(sub),ロール(role)などが含まれる。roleの値は,会員が member,運用担当者が operator,配送員が partner である。各APIは,リクエストのAuthorizationヘッダーに含まれるアクセストークンを検証し,subの値によって利用者を識別する。アクセストークンの例を図1に,会員APIのトークン検証設定を図2に示す。

図1 アクセストークンの例(Base64URLデコード後)

(例1) 会員アプリで取得したアクセストークン
ヘッダー  : {"alg":"RS256","typ":"JWT","kid":"k2026a"}
ペイロード: {"iss":"https://auth.mealbox.example","sub":"M0012345",
             "aud":"https://api.mealbox.example","role":"member",
             "iat":1789000000,"exp":1789000900}

(例2) 配送ポータルで取得したアクセストークン
ヘッダー  : {"alg":"RS256","typ":"JWT","kid":"k2026a"}
ペイロード: {"iss":"https://auth.mealbox.example","sub":"D0000777",
             "aud":"https://partner-api.mealbox.example","role":"partner",
             "iat":1789000100,"exp":1789001000}

図2 会員APIのトークン検証設定(現行)

token_verify:
  jwks_uri  : https://auth.mealbox.example/.well-known/jwks.json
  issuer    : https://auth.mealbox.example   # issが一致しないトークンは拒否
  algorithms: from_header                    # トークンのヘッダーのalgに従って検証
  audience  : (設定なし)                     # audは検証しない
  leeway_sec: 60                             # exp検証の時刻ずれの許容(秒)

会員APIの仕様の抜粋を表1に示す。

表1 会員APIの仕様(抜粋)

項番メソッドとパス機能仕様書に記載された利用者
1GET /v1/orders/{order_id}注文の詳細を返す会員(自分の注文だけ)
2GET /v1/members/me自分の会員情報を返す会員
3PATCH /v1/members/me自分の会員情報(氏名,住所,電話番号,配達指示)を変更する会員
4GET /v1/admin/members?page=&size=全会員の一覧を返す運用担当者
5POST /v1/auth/reset/verifyパスワード再設定用の確認コードを検証する未ログインの会員

注記 order_idは,注文ごとに1ずつ増える8桁の整数である。項番5の確認コードは,再設定の申込み時に会員のメールアドレスに送られる6桁の数字(000000〜999999)であり,有効期間は10分である。

〔API診断の実施〕

F社は,新しい会員アプリの公開を前に,セキュリティ診断会社のG社に会員APIの診断を委託した。診断は検証環境で行い,G社の診断員は,F社が用意した二つのテスト用会員アカウント(会員A,会員B),テスト用配送員アカウント及びテスト用運用担当者アカウントを使用した。診断結果を表2に示す。

表2 診断結果(抜粋)

No.対象診断の内容と結果
1表1の項番1会員Aのアクセストークンで,order_idを会員Bの注文の番号に変えてリクエストしたところ,会員Bの氏名,住所,電話番号を含む注文の詳細が返された。
2表1の項番4会員Aのアクセストークンでリクエストしたところ,全会員の一覧が返された。運用画面以外からの呼出しは想定されていなかった。
3表1の項番2,3項番2のレスポンスに,会員アプリが表示しない項目(割引率 discount_rate,問合せ対応メモ internal_memo)が含まれていた。また,項番3で{"discount_rate":50}を送ったところ,割引率が50%に変更された。
4表1の項番1〜4ヘッダーを{"alg":"none","typ":"JWT"}とし,ペイロードのroleをoperatorに変え,署名部を空にしたトークンが受理された。
5表1の項番1〜4図1の(例2)のような配送員のアクセストークンが受理された。
6表1の項番51秒当たり100回の検証リクエストを送り続けても,全て処理された。HTTPステータスコード429などによる拒否はなく,確認コードは誤りが続いても無効にならなかった。

G社は,診断報告書で,各指摘にOWASP API Security Top 10(2023年版)の分類を併記した。No.1はAPI1:2023 Broken 【 a 】 Level Authorization,No.2はAPI5:2023 Broken 【 b 】 Level Authorization,No.3はAPI3:2023 Broken Object 【 c 】 Level Authorizationに当たる。No.6については,10分間に試行できる回数から計算すると,攻撃者が会員の確認コードを総当たりで当てる確率は,1回の申込み当たり約【 d 】%であり,申込みを繰り返せば更に高くなると指摘した。

〔修正方針の検討〕

F社のJ課長と開発チームのKさんは,診断報告書を基に修正方針を検討した。2週間後に新しい会員アプリの公開とキャンペーンを控えており,開発チームの工数は限られている。

No.1について,Kさんは,①order_idを推測困難なランダムな値(UUID)に変更すれば解決すると提案した。しかし,G社は,これだけでは不十分だと指摘した。そこで,表3の修正案を比較した。

表3 No.1の修正案

案内容
アorder_idをUUIDに変更する。
イリクエストのクエリパラメータにmember_idを追加させ,その値がアクセストークンのsubと一致する場合だけ注文を返す。
ウデータベースから取得した注文の会員ID(member_id)がアクセストークンのsubと一致する場合だけ注文を返し,一致しない場合は注文が存在しない場合と同じ応答を返す。
エ会員アプリと会員SPAの画面に,他の会員の注文番号が表示されないようにする。

No.2について,Kさんは,会員アプリには運用画面へのメニューがないので影響は小さいと考えていた。しかし,J課長は,攻撃者は会員アプリを使わずにAPIを直接呼び出せると指摘し,②会員API側で確認を追加することにした。ただし,G社は,③No.4が残ったままでは,②の確認を追加しても効果がないと説明した。

No.4とNo.5については,図2の設定を,algorithmsを [ 【 e 】 ] に,audienceを 【 f 】 に変更することにした。

No.3について,会員情報を変更する処理の実装を図3に示す。

図3 表1の項番3の実装(抜粋)

// PATCH /v1/members/me
const member = await Member.findById(token.sub);
Object.assign(member, req.body);   // ④リクエストの項目をそのまま会員レコードに反映
await member.save();
res.json(member);                  // 会員レコードの全項目を返す

No.6について,Kさんは,⑤APIの手前のリバースプロキシで,送信元IPアドレスごとに1秒当たり10回を超えるリクエストを拒否する案を示した。G社は,この案だけでは不十分であり,確認コード単位の対策も必要だと助言した。

設問1(1)

本文中の【 a 】〜【 c 】に入れる適切な字句を,解答群の中から選び,記号で答えよ。 解答群 ア Function イ Object ウ Property エ Resource オ Role カ Session

設問1(2)

本文中の【 d 】に入れる適切な数値を,整数で答えよ。なお,攻撃者は表2のNo.6で確認できた1秒当たり100回の速さで試行し,同じ値を重複して試さず,確認コードの有効期間の全てを試行に使えるものとする。また,確認コードは000000〜999999から無作為に選ばれるものとする。

設問2(1)

〔修正方針の検討〕の下線①について,G社がこれだけでは不十分だと指摘した理由を,40字以内で答えよ。

0 / 40字

設問2(2)

表3の修正案のうち,No.1の脆弱性の修正として最も適切なものを選び,記号で答えよ。

設問3(1)

〔修正方針の検討〕の下線②について,No.2の運用担当者用API(表1の項番4など)を呼び出されたときに,会員APIで何を確認すべきか。本文中の字句を用いて,40字以内で答えよ。

0 / 40字

設問3(2)

〔修正方針の検討〕の下線③について,効果がない理由を,表2の内容を基に50字以内で答えよ。

0 / 50字

設問4

本文中の【 e 】,【 f 】に入れる適切な字句を答えよ。

設問5

図3中の下線④について,表2のNo.3のうち割引率が変更された問題を解消する修正方法を,40字以内で答えよ。

0 / 40字

設問6

〔修正方針の検討〕の下線⑤について,この案だけでは不十分である理由を,45字以内で答えよ。

0 / 45字

自己採点: ◯ 0 / △ 0 / ✕ 0(全9設問)

書いた解答は、この端末のブラウザにだけ保存されます(同期されません)。