なぜ危ないのか
AIが生成するHTMLには、デモ用のサンプルコードとして`<script>alert(document.cookie)</script>`相当のインラインスクリプトが含まれることがある。これをそのまま社外の関係者に送ると、相手がそのURLをブラウザで開いた瞬間にスクリプトが実行され、セッション情報やlocalStorageのトークンが外部へ送信される恐れがある。
特に問題になるのは、AIにフォームUIを生成させたときだ。入力値をそのまま`innerHTML`に代入するパターンが生まれやすく、レビュアーが入力欄に文字列を打ち込んだだけでXSSが発火する。社外の相手は自社のセキュリティポリシーの外にいるため、被害が発覚しにくい点も注意が必要だ。
「noindexを設定したから検索には出ない」という理由でURLをばらまくケースがあるが、XSSはインデックス化とは無関係に発動する。認証なしで誰でも開けるURLである限り、スクリプトは誰に対しても実行可能な状態になっている。
- 相手がログインなしで開ける状態か確認する
- PCとスマホで最低1回ずつ表示を確認する
- 内部情報・個人情報・不要な外部送信が残っていないか見る
- レビュー期限と修正時の差し替え方を決めておく
ソースで見る場所
HTMLファイルをテキストエディタで開き、`<script`タグを全件検索する。`src`属性のない、つまりインラインのscriptブロックが含まれている場合は、その中身を読んで`innerHTML`への代入・`document.write`・`eval`の三つがないかを確認する。一つでも見つかれば要修正と判断する。
次に`onerror`・`onclick`・`onload`などのイベントハンドラ属性を正規表現`on[a-z]+=`で検索する。AIは画像の遅延ロードやボタンのフィードバック表示にこれらを使いがちで、属性値の中にJSが埋め込まれている場合がXSSの温床になる。VSCodeなら正規表現検索を有効にしてCtrl+Shift+Hで一括確認できる。
外部scriptのURLも確認対象だ。`<script src="https://cdn.example.com/...">`のように外部ドメインからJSを読み込んでいる場合、そのCDNが乗っ取られるとサプライチェーン攻撃につながる。公開前は`src`属性のドメインをリストアップし、信頼できるドメインのみかを突き合わせる。
- HTML内の外部script・form action・iframeを確認する
- APIキーやトークンらしき文字列がないか検索する
- 画像・CSS・JSのパスが公開後も解決できるか見る
- noindexと認証を混同せず、必要なら両方設定する
安全に共有する設定
XSSのリスクを排除したうえで安全に共有するには、URLに認証を付けることが最低限の前提になる。ギガサイト便のようなパスワード付きプレビューURLを使えば、URLを知っているだけでは開けないため、万が一URLが転送されても第三者がスクリプトを実行する機会を減らせる。
Content Security Policyのヘッダをサーバー側で付与できる環境なら、`script-src 'self'`を設定してインラインスクリプトを実行禁止にする方法が有効だ。Cloudflare PagesやNetlifyではHTMLファイルと同階層に`_headers`ファイルを置くだけで適用できるため、デプロイのついでに設定しておくと今後の事故を防ぎやすい。
共有後の残存リスクを管理するために、レビュー期限を明示してURLを伝えることも重要だ。「〇日以降はアクセス不可」と事前に合意しておけば、レビュー終了後にURLを失効させるタイミングが明確になり、不必要な公開期間を最小化できる。
- HTML内の外部script・form action・iframeを確認する
- APIキーやトークンらしき文字列がないか検索する
- 画像・CSS・JSのパスが公開後も解決できるか見る
- noindexと認証を混同せず、必要なら両方設定する
再発防止ルール
AIにHTMLを生成させるプロンプトの末尾に「JavaScriptは外部ファイルに分離し、インラインscriptとイベントハンドラ属性は使わないこと」という制約を常に付け加えるのが最も簡単な再発防止策だ。この一文を追加するだけで、生成物からinline scriptが除去される確率が大きく上がる。
チーム内でHTMLを社外共有するたびにXSSチェックを実施するよう、GitHubのPRテンプレートや共有用チェックリストに項目として追加しておく。「社外URLを発行する前にinlineスクリプト確認済み」というチェックボックスを設けることで、担当者が変わっても手順が引き継がれやすくなる。
過去に生成したHTMLが残っている場合は、一括スキャンも検討する。`grep -rn 'innerHTML\|eval\|document.write' ./html-files/`をCI上で定期実行するか、linterルールとして追加すると、蓄積したファイルの中に潜むXSSを継続的に発見できる。
- HTML内の外部script・form action・iframeを確認する
- APIキーやトークンらしき文字列がないか検索する
- 画像・CSS・JSのパスが公開後も解決できるか見る
- noindexと認証を混同せず、必要なら両方設定する
よくある質問
社外レビュー用URLにパスワードを設定しても、XSS対策として十分ですか?
パスワードはURLへの不正アクセスを防ぐが、XSS自体は除去しない。パスワードを知るレビュアーがページを開いた時点でスクリプトは動くため、ソースのインラインJS排除とアクセス制限の両方が必要だ。
AIが生成したinline scriptがすべて危険なわけではないと思いますが、どう判断すれば良いですか?
スクリプトの内容が`innerHTML`代入・eval・外部fetch/XHRを含まない純粋なDOM操作(クラス付け替えなど)であれば即時のXSSリスクは低い。ただし社外共有前は内容を問わず除去するか外部ファイル化するのが原則だ。
Cloudflare Pagesでホストする場合、CSPヘッダはどのファイルに書けば反映されますか?
プロジェクトルート直下の`_headers`ファイルにパスとヘッダを記述する。例えば`/* Content-Security-Policy: script-src 'self'`と書けば全URLに適用される。デプロイ後にブラウザの開発者ツールでレスポンスヘッダを確認して反映を検証できる。