情報処理安全確保支援士 2023年 秋期 午後 問01
Webアプリケーションプログラムの開発に関する次の記述を読んで、設問に答えよ。
Q社は、洋服のEC事業を手掛ける従業員100名の会社である。WebアプリQというWebアプリケーションプログラムでECサイトを運営している。ECサイトのドメイン名は“□□□.co.jp”であり、利用者はWebアプリQにHTTPSでアクセスする。WebアプリQの開発と運用は、Q社開発部が行っている。今回、WebアプリQに、ECサイトの会員による商品レビュー機能を追加した。図1は、WebアプリQの主な機能である。


ある日、会員から、無地Tシャツのレビューページ(以下、ページVという)に16件表示されるはずのレビューが2件しか表示されていないという問合せが寄せられた。開発部のリーダーであるNさんがページVを閲覧してみると、画面遷移上おかしな点はなく、図2が表示された。

WebアプリQのレビューページでは、次の項目がレビューの件数分表示されるはずである。
・レビューを投稿した会員のアイコン画像
・レビューを投稿した会員の表示名
・レビューが投稿された日付
・レビュー評価(1~5個の★)
・会員が入力したレビュータイトル
・会員が入力したレビュー詳細
不審に思ったNさんはページVのHTMLを確認した。図3は、ページVのHTMLである。


図3のHTMLを確認したNさんは、会員Aによって15件のレビューが投稿されていること、及びページVには長いスクリプトが埋め込まれていることに気付いた。Nさんは、ページVにアクセスしたときに生じる影響を調査するために、アクセスしたときにWebブラウザで実行されるスクリプトを抽出した。図4は、Nさんが抽出したスクリプトである。

Nさんは、会員Aの投稿はクロスサイトスクリプティング(XSS)脆弱性を悪用した攻撃を成立させるためのものであるという疑いをもった。NさんがWebアプリQを調べたところ、WebアプリQには、会員が入力したスクリプトが実行されてしまう脆弱性があることを確認した。加えて、WebアプリQがcookieにHttpOnly属性を付与していないこと及びアップロードされた画像ファイルの形式をチェックしていないことも確認した。
Q社は、必要な対策を施し、会員への必要な対応も行った。
設問1:この攻撃で使われたXSS脆弱性について答えよ。
(1)XSS脆弱性の種類を解答群の中から選び、記号で答えよ。
解答群
ア:DOM Based XSS
イ:格納型XSS
ウ:反射型XSS
模範解答
イ
解説
解答の論理構成
- まず攻撃コードがどこに置かれているかを確認します。問題文には
「“ページVには長いスクリプトが埋め込まれている”」
「“会員Aによって15件のレビューが投稿されている”」
とあり、悪意ある JavaScript がレビュー本文としてサーバ側に保存されていることが分かります。 - 保存されたレビューは、ページVを閲覧したすべての利用者のブラウザに配信され、そこで実行されます。これは問題文の
「“アクセスしたときにWebブラウザで実行されるスクリプトを抽出した”」
から明らかです。実際、図4 のスクリプトは閲覧者のクッキーを含むファイルをアップロードする処理を行っています。 - 以上より、攻撃コードは
・サーバに“格納”され
・第三者のリクエストに“反射”されるのではなくそのまま配信され
・結果として閲覧者側で自動実行される
という流れです。 - この性質は、一般に Stored/Persistent XSS と呼ばれるものであり、解答群で該当するのは「イ:格納型XSS」です。
誤りやすいポイント
- URL パラメータや検索結果が関わると reflexive と即断してしまうが、本問ではレビューという恒常的なデータ領域が利用されている点を見落としやすいです。
- DOMBased と混同しやすいですが、DOMBased はクライアント側の JavaScript が location.hash などを直接解析してスクリプトを生成するケースです。本問ではサーバから送られた HTML に既にスクリプトが含まれており該当しません。
- 「閲覧をトリガに実行される=反射型」と誤認するケースがあります。実行タイミングではなく“保管場所”と“配信方法”に着目することが重要です。
FAQ
Q: 投稿した本人だけでなく他の会員にも実行されるのはなぜですか?
A: レビューはサーバのデータベースに保存され、そのまま HTML として再配信されるため、ページVを開いた全ユーザのブラウザでスクリプトが動きます。
A: レビューはサーバのデータベースに保存され、そのまま HTML として再配信されるため、ページVを開いた全ユーザのブラウザでスクリプトが動きます。
Q: HttpOnly 属性が無い点は XSS の種類判定に関係しますか?
A: 判定そのものには関係しません。HttpOnly の欠如はクッキー窃取を容易にする“被害拡大要因”であり、脆弱性の分類(格納型・反射型・DOMBased)には影響しません。
A: 判定そのものには関係しません。HttpOnly の欠如はクッキー窃取を容易にする“被害拡大要因”であり、脆弱性の分類(格納型・反射型・DOMBased)には影響しません。
Q: 図4 が XMLHttpRequest を使っているのは珍しいのですか?
A: 手口としては典型的です。格納型 XSS でスクリプトを仕込み、XHR で document.cookie を含むファイルをアップロードすることでセッション固定や乗っ取りを狙う例は古くから報告されています。
A: 手口としては典型的です。格納型 XSS でスクリプトを仕込み、XHR で document.cookie を含むファイルをアップロードすることでセッション固定や乗っ取りを狙う例は古くから報告されています。
関連キーワード: XSS, cookie, XMLHttpRequest, FormData, セッションID
設問1:この攻撃で使われたXSS脆弱性について答えよ。
(2)WebアプリQにおける対策を、30字以内で答えよ。
模範解答
レビュータイトルを出力する前にエスケープ処理を施す。
解説
解答の論理構成
- 攻撃の原因
- 【問題文】では「WebアプリQには、会員が入力したスクリプトが実行されてしまう脆弱性がある」と明示しています。
- 図3の HTML 断片には Goodがそのまま出力されており、会員が自由記述できる「レビュータイトル」に
- 脆弱性の種類
- 「クロスサイトスクリプティング(XSS)脆弱性」を悪用され、ブラウザが攻撃者のスクリプトを実行しています。
- 発生箇所の特定
- 攻撃コードは「レビュータイトル」フィールド経由で埋め込まれたため、出力時に無加工で HTML に挿入される設計が問題です。
- 求められる対策
- 入力値を保存する際ではなく、「レビュータイトルを出力する前」に HTML エスケープを行えば、< や > が文字列として表示され、スクリプトは実行されません。
- したがって模範解答「レビュータイトルを出力する前にエスケープ処理を施す。」となります。
誤りやすいポイント
- 入力値検証だけで十分と誤解し、「出力時エスケープ」の重要性を見落とす。
- JavaScript の innerText 代入など、実装手段を答えてしまい設問の趣旨から逸脱。
- HttpOnly 属性の設定や画像形式チェックを対策と勘違いし、本設問の焦点(XSS対策)から外れる。
FAQ
Q: なぜ保存時のフィルタリングではなく出力時エスケープを強調しているのですか?
A: 保存時に正規化すると、表示場所ごとに必要なサニタイズ方法が異なり破綻しやすいからです。出力時なら位置に合わせた適切なエスケープを確実に適用できます。
A: 保存時に正規化すると、表示場所ごとに必要なサニタイズ方法が異なり破綻しやすいからです。出力時なら位置に合わせた適切なエスケープを確実に適用できます。
Q: Content-Security-Policy を設定すれば十分ですか?
A: CSP は強力な追加防御ですが、根本的な対策はユーザ入力を無害化して HTML に出力することです。本問の要求はまずエスケープ処理です。
A: CSP は強力な追加防御ですが、根本的な対策はユーザ入力を無害化して HTML に出力することです。本問の要求はまずエスケープ処理です。
Q: DOMPurify などのライブラリ使用でも良いですか?
A: はい。ライブラリであっても「レビュータイトルを出力する前にエスケープ処理を施す」という方針を満たせば適切と判断されます。
A: はい。ライブラリであっても「レビュータイトルを出力する前にエスケープ処理を施す」という方針を満たせば適切と判断されます。
関連キーワード: XSS, エスケープ、DOM, Cookie, セッションID
設問2:
図3について、入力文字数制限を超える長さのスクリプトが実行されるようにした方法を、50字以内で答えよ。
模範解答
HTMLがコメントアウトされ一つのスクリプトになるような投稿を複数回に分けて行った。
解説
解答の論理構成
- 攻撃者である「会員A」は、同じ商品に「15件」のレビューを投稿している(図3より判明)。
- 各レビューの「レビュータイトル」に
- WebアプリQには「会員が入力したスクリプトが実行されてしまう脆弱性」があるため、HTMLエスケープが行われず、投稿内容がそのままページに埋め込まれる。
- レビューは一覧表示時に連続して並ぶため、コメントアウトで“つなげた”各レビュータイトルの内容がブラウザ上では1本の
- この手法ならば、各入力欄の制限長を超えるコードも“複数回に分けて”注入できる。
- 以上より「HTMLがコメントアウトされ一つのスクリプトになるような投稿を複数回に分けて行った。」が適切な解答となる。
誤りやすいポイント
- 1件のレビューだけでスクリプト全体を埋め込んだと考えてしまい、コメントアウトの役割を見落とす。
- コード長制限の回避方法を「改行を入れた」と誤記し、分割投稿という本質を外す。
- XSS 対策不足(エスケープ不足)と Cookie 盗難(HttpOnly 未設定)を混同し、本問の「文字数制限突破」の着眼点から逸れる。
FAQ
Q: コメントアウトを利用するとブラウザはどのように動作しますか?
A: /* から */ まではコメントとみなしスクリプトを実行しません。そのため複数の入力欄に交互に配置すると、実行対象コードとコメントが意図どおりに結合されます。
A: /* から */ まではコメントとみなしスクリプトを実行しません。そのため複数の入力欄に交互に配置すると、実行対象コードとコメントが意図どおりに結合されます。
Q: レビュー詳細欄ではなくタイトル欄が狙われたのはなぜですか?
A: タイトルは一覧表示で連続して並ぶため、分割したコードを確実に隣接させられ、ブラウザが1本の
A: タイトルは一覧表示で連続して並ぶため、分割したコードを確実に隣接させられ、ブラウザが1本の
Q: コード長制限への対策は何をすべきですか?
A: サーバ側で入力内容を HTML エスケープするほか、スクリプトタグやイベントハンドラ文字列を検知・除去するフィルタリングを併用し、単純な文字数制限だけに頼らないことが重要です。
A: サーバ側で入力内容を HTML エスケープするほか、スクリプトタグやイベントハンドラ文字列を検知・除去するフィルタリングを併用し、単純な文字数制限だけに頼らないことが重要です。
関連キーワード: XSS, コメントアウト、Scriptインジェクション、Cookie盗難、XMLHttpRequest
設問3:図4のスクリプトについて答えよ。
(1)図4の6〜20行目の処理の内容を、60字以内で答えよ。
模範解答
XHRのレスポンスから取得したトークンとともに、アイコン画像としてセッションIDをアップロードする。
解説
解答の論理構成
- 図4の6:行目で xhr.onload = function() が定義され、1回目のリクエスト完了後に以降の処理が実行されます。
- 7:行目で page = xhr.response; とし、4:行目の xhr.responseType = "document"; によって取得した DOM を格納します。
- 8:行目の token = page.getElementById("token").value; で、会員プロフィール画面に埋め込まれている CSRF 対策用トークンを抽出します。
- 9:〜11:行目で xhr2 を生成し、url2 = "https://[□□□].co.jp/user/upload"; へ POST 送信する準備をします。
- 12:〜18:行目では FormData を組み立てます。特に
・13:行目で cookie = document.cookie; として被害者ブラウザ内の cookie(セッションIDを含む)を取得
・16:行目で file = new File([cookie]、fname, {type: ftype}); とし、cookie 文字列をファイル内容とする擬似画像を生成
・17:行目で form.append("uploadfile"、file); としてファイルを、18:行目で form.append("token"、token); としてトークンを追加 - 19:行目の xhr2.send(form); でアップロードを実行し、被害者のセッションIDを画像ファイルとしてサーバへ送信します。
- 以上より「1 回目の XHR でトークンを取得 → 自身の cookie をファイルに偽装 → 正規トークン付きでアップロード」という攻撃フローになるため、模範解答の内容となります。
誤りやすいポイント
- document.cookie の送信を「直接 POST パラメータで送る」と誤解し、ファイルオブジェクト化している点を見落とす。
- token を CSRF トークンではなく「ファイル名」や「パスワード」と読み違える。
- xhr.responseType = "document" の意味を把握できず、HTML 解析を伴うことに気付かない。
- 1 回目と 2 回目の XMLHttpRequest を区別できず、両者の役割を混同する。
FAQ
Q: なぜセッションIDを画像ファイルに偽装して送るのですか?
A: アップロード API は画像ファイルしか受け付けないため、File オブジェクトを作成して中身に cookie を詰め、形式だけ image/png にすることでバイパスしています。
A: アップロード API は画像ファイルしか受け付けないため、File オブジェクトを作成して中身に cookie を詰め、形式だけ image/png にすることでバイパスしています。
Q: token を取得する目的は何ですか?
A: "https://□□□.co.jp/user/upload" は CSRF 対策として "token" パラメータを要求します。攻撃者は 1 回目のリクエストで正規トークンを盗み、2 回目のアップロードを正当なリクエストに見せかけています。
A: "https://□□□.co.jp/user/upload" は CSRF 対策として "token" パラメータを要求します。攻撃者は 1 回目のリクエストで正規トークンを盗み、2 回目のアップロードを正当なリクエストに見せかけています。
Q: この攻撃で最終的に何が危険になるのですか?
A: 攻撃者は被害者のセッションIDを自分が閲覧できる場所(たとえば自分のアカウントのアイコン画像)に保存させ、後で取得して乗っ取ることが可能になります。
A: 攻撃者は被害者のセッションIDを自分が閲覧できる場所(たとえば自分のアカウントのアイコン画像)に保存させ、後で取得して乗っ取ることが可能になります。
関連キーワード: XSS, XMLHttpRequest, cookie, CSRFトークン、セッションID
設問3:図4のスクリプトについて答えよ。
(2)攻撃者は、図4のスクリプトによってアップロードされた情報をどのようにして取得できるか。取得する方法を、50字以内で答えよ。
模範解答
会員のアイコン画像をダウンロードして、そこからセッションIDの文字列を取り出す。
解説
解答の論理構成
- 図4のスクリプトでは document.cookie を変数 cookie に格納し(行13)、被害者ブラウザ内のセッション ID などを取得します。
- 行16で new File([cookie]、fname, {type: ftype}); を実行し、cookie 文字列を内容とするファイル a.png(MIME タイプは "image/png")を生成します。
- 行17で form.append("uploadfile"、file); として、レビュー機能と同一サイト内のアイコン画像アップロード API である "https://□□□.co.jp/user/upload"(行10)へ送信します。
- 図1の説明より、アップロードされたアイコン画像は「会員プロフィール設定ページやレビュー専用ページに表示される」とあります。つまりファイルは公開 URL /users/●●●/icon.png で誰でも参照可能になります。
- 攻撃者は後から当該 URL をダウンロードするだけで、画像の中身として保存された cookie(=被害者のセッション ID)を取得できます。
- したがって問われている「アップロードされた情報の取得方法」は「公開されたアイコン画像を取得して中身を読む」ことになります。
誤りやすいポイント
- XHR2 のレスポンスを利用して情報を直接窃取すると誤解しやすいが、実際にはアイコン画像として保存されたファイルを後から盗み見る手口です。
- 「HttpOnly が付いていないから cookie を読める」点は条件であって、取得方法そのものではありません。
- ファイル名や MIME タイプを画像に偽装しているため、画像ビューアで開くと空白に見えて気付きにくいことがあります。
FAQ
Q: なぜ被害者ユーザのパスで保存された画像 URL を攻撃者が知り得るのですか?
A: 多くのサイトではプロフィールページの HTML にアイコン画像の絶対パスが埋め込まれるため、攻撃者が同ページを閲覧すれば URL を得られます。
A: 多くのサイトではプロフィールページの HTML にアイコン画像の絶対パスが埋め込まれるため、攻撃者が同ページを閲覧すれば URL を得られます。
Q: もし HttpOnly 属性が付与されていたら、この攻撃は成立しますか?
A: document.cookie が取得できなくなるため、少なくともセッション ID を画像に書き込む部分は失敗します。
A: document.cookie が取得できなくなるため、少なくともセッション ID を画像に書き込む部分は失敗します。
Q: 画像形式チェックが有効なら防げますか?
A: チェックが拡張子や MIME タイプだけなら回避可能です。バイナリ検査で PNG シグネチャを検証すれば無効化できます。
A: チェックが拡張子や MIME タイプだけなら回避可能です。バイナリ検査で PNG シグネチャを検証すれば無効化できます。
関連キーワード: XSS, CSRF, セッションID, MIMEタイプ偽装、cookie盗聴
設問3:図4のスクリプトについて答えよ。
(3)攻撃者が(2)で取得した情報を使うことによってできることを、40字以内で答えよ。
模範解答
ページVにアクセスした会員になりすまして、WebアプリQの機能を使う。
解説
解答の論理構成
- スクリプトが取得する情報
- 図4 行 13: cookie = document.cookie; でブラウザに保存された cookie を取得しています。
- その cookie が意味するもの
- 【問題文】図1「ログイン機能」には
“ログインした会員には、セッションIDを cookie として払い出す。”
とあり、cookie 内には会員の セッションID が格納されています。
- 【問題文】図1「ログイン機能」には
- 攻撃者が cookie を入手できる理由
- 図4 行 17: form.append("uploadfile"、file); で cookie 文字列をファイル内容にしてアップロードし、行 19: xhr2.send(form); で攻撃者が用意した https://[□□□].co.jp/user/upload に送信します。
- WebアプリQは「cookieにHttpOnly属性を付与していない」ため、JavaScript から自由に読み取れます。
- セッションIDを得た攻撃者が可能になる行為
- セッションIDは本人確認に利用される唯一の情報であり、これを保持すればサーバはアクセス元を会員本人だと誤認します。
- その結果、攻撃者は 「ページVにアクセスした会員になりすまして、WebアプリQの機能を使う。」 ことができます。
誤りやすいポイント
- 「token だけで攻撃できる」と誤解し、cookie の重要性を見落とす。
- 「画像アップロードだから無害」と考え、ファイルにセッションIDが埋め込まれる点に気付かない。
- HttpOnly 属性が付いていない設定を読み飛ばし、「JavaScript から cookie は読めない」と判断してしまう。
- CSRF と混同し、「被害は勝手な投稿が行われる程度」と限定的に捉える。
FAQ
Q: なぜ攻撃者サイトではなく https://□□□.co.jp 宛てにアップロードしているのですか?
A: 同一生成元ポリシーを回避するためです。正規ドメインへの送信であればブラウザは警告を出さず、攻撃者は後でサーバからファイルを取得して内容(cookie)を読めます。
A: 同一生成元ポリシーを回避するためです。正規ドメインへの送信であればブラウザは警告を出さず、攻撃者は後でサーバからファイルを取得して内容(cookie)を読めます。
Q: HttpOnly 属性を付ければ今回の攻撃は防げますか?
A: JavaScript から cookie を読めなくなるので、少なくともセッションIDの窃取は防止できます。ただし他にも XSS やアップロード処理の検証不足など複数の問題があるため、包括的な対策が必要です。
A: JavaScript から cookie を読めなくなるので、少なくともセッションIDの窃取は防止できます。ただし他にも XSS やアップロード処理の検証不足など複数の問題があるため、包括的な対策が必要です。
Q: 画像形式チェックが甘い点は何と関係していますか?
A: 攻撃者が実際には テキストファイル(セッションID入り)を image/png と偽ってアップロードできるため、サーバに敏感情報を保存させるのに利用されています。
A: 攻撃者が実際には テキストファイル(セッションID入り)を image/png と偽ってアップロードできるため、サーバに敏感情報を保存させるのに利用されています。
関連キーワード: XSS, セッションハイジャック、HttpOnly, Same-Origin Policy, FormData
設問4:
仮に、攻撃者が用意したドメインのサイトに図4と同じスクリプトを含むHTMLを準備し、そのサイトにWebアプリQのログイン済み会員がアクセスしたとしても、Webブラウザの仕組みによって攻撃は成功しない。この仕組みを、40字以内で答えよ。
模範解答
スクリプトから別ドメインのURLに対してcookieが送られない仕組み
解説
解答の論理構成
- 図4のスクリプトは
2: url1 = "https://[□□□].co.jp/user/profile";
に対して XMLHttpRequest を発行し、レスポンスから 8: token = page.getElementById("token").value; を取得しようとしています。 - ところが、想定シナリオではこの JavaScript が「攻撃者が用意したドメイン」で実行され、https://[□□□].co.jp とは別ドメインになります。
- ブラウザは別ドメインに対して送信する XMLHttpRequest では、明示的に
xhr.withCredentials = true;
を指定しない限り、当該ドメインの cookie(セッション ID など)を自動送信しません。図4にはこの設定が存在しません。 - その結果、https://[□□□].co.jp/user/profile へのリクエストにはログイン状態を示す cookie が付かず、サーバは会員ページを表示せずにログインページ等を返します。よって page.getElementById("token") は取得できず、続く
18: form.append("token"、token);
も失敗し、攻撃は成立しません。 - 以上より、設問が求める “Webブラウザの仕組み” は
スクリプトから別ドメインの URL に対して cookie が送られない仕組み
であると論理的に導けます。
誤りやすいポイント
- 「同一生成元ポリシーでレスポンスが読めない」と思い込み、cookie 送信の可否を見落とす。
- HttpOnly 属性の有無が原因と誤解する(ここでは cookie がそもそも送られないことがポイント)。
- withCredentials の既定値が false であることを忘れ、クロスサイトでも cookie が付くと考えてしまう。
FAQ
Q: xhr.withCredentials = true; を攻撃者が追加すれば成立しますか?
A: 送信自体は可能になりますが、サーバ側で CORS 設定が無い場合、レスポンスを JavaScript で読むことはできません。さらなる条件が必要です。
A: 送信自体は可能になりますが、サーバ側で CORS 設定が無い場合、レスポンスを JavaScript で読むことはできません。さらなる条件が必要です。
Q: 同一生成元ポリシーと今回の仕組みは何が違いますか?
A: 同一生成元ポリシーは「別ドメインのレスポンスを JS が参照できない」仕組み、今回のポイントは「別ドメインへリクエストする際に cookie が自動送信されない」仕組みです。
A: 同一生成元ポリシーは「別ドメインのレスポンスを JS が参照できない」仕組み、今回のポイントは「別ドメインへリクエストする際に cookie が自動送信されない」仕組みです。
Q: ブラウザをまたがずにサーバ側だけで防ぐ方法はありますか?
A: SameSite=Lax や SameSite=Strict を cookie に付与すれば、クロスサイトリクエスト時の送信をブラウザ任せで一段と制限できます。
A: SameSite=Lax や SameSite=Strict を cookie に付与すれば、クロスサイトリクエスト時の送信をブラウザ任せで一段と制限できます。
関連キーワード: Same-Origin Policy, CORS, XMLHttpRequest, withCredentials, セッションID


