セキュリティマガジン盾 - TATE -

JavaScriptのinnerHTMLは危険?XSS攻撃が成立する条件を検証

最終更新日 2026年01月12日 投稿日 2025年12月20日
innerHTMLにXSS攻撃する画像

innerHTMLは、HTML文字列をそのまま画面に書き換えられる便利な機能ですが、使い方を誤るとXSSクロスサイトスクリプティング)の入口になります。

本記事では、innerHTMLの基本的な挙動を押さえたうえで、なぜ「危険」「非推奨」と言われるのかを、DOM-based XSSの考え方(ソース/シンク)から整理します。
さらにinnerHTMLを使用した最小構成のデモを作り、ローカル環境で<script>タグやイベントハンドラ型のXSS攻撃が成立するのか実際に検証します。

最後に検証結果を元にXSS攻撃の対策も解説しますので、innerHTMLを開発アプリで使用している方JavaScript初学者の方は是非最後までご覧になってください。

目次

JavaScriptのinnerHTMLとは?できることと基本の使い方

innerHTMLは、要素の中身(HTML)を文字列で取得・書き換えできるJavaScriptのプロパティです。

const box = document.querySelector("#box"); // 取得(HTML文字列として取り出す) const current = box.innerHTML; console.log(current); // 書き換え(HTMLとして解釈されて表示される) box.innerHTML = "<b>こんにちは</b>";

上記のソースコードのように、現在の内容を取得してログに出したり、HTMLタグを含む文字列を代入して画面を更新したりすることができます。

なぜinnerHTMLは「危険」「非推奨」と言われるのか

innerHTMLが「危険」「非推奨」と言われるのは、文字列をHTMLとして解釈してそのままDOMを書き換えできるためです。

const out = document.querySelector("#out"); const user = document.querySelector("#name").value; // 取得 // 危険:HTMLとして解釈され、意図しない挙動を招く可能性 out.innerHTML = user;

上記のソースコードのようにユーザー入力を取得してinnerHTMLに代入すると、表示ではなく実行される文脈に入ってしまい、XSSが成立する可能性があります。

本記事では、このinnerHTMLでXSS攻撃が成立する条件について、ローカル環境で実際に検証していきます。

検証の準備:最小構成のデモアプリを作成

それでは、innerHTMLにXSS攻撃が通るのか検証していきます。

検証で使用するソースコードについては、GitHubからも取得できますので必要な方は下記リンクをご確認ください。
検証ソースコード(GitHubリンク)

注意事項

本記事の検証は学習目的のため、必ず自分のローカル環境・許可された範囲で行ってください。許可なく他人のサイトやサービスにXSSを試す行為は不正アクセス等に該当する可能性があります

検証環境

OS:Windows 11 Home
実行環境:Docker(ローカル環境で実行)
ブラウザ:Google Chrome
使用言語:JavaScript,CSS,HTML

検証画面構成

デモ画面
検証画面は上記のように、入力欄と出力欄がある構成になっています。

デモ実行画面
上記のように、入力欄に文字列を入力して投稿ボタンを押下すると、投稿結果表示の下に入力した文字列が表示される構成となっております。

ソースコード解説

<!-- index.html --> <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>XSS Test</title> <link rel="stylesheet" href="style.css"> </head> <body> <div class="container"> <h1 class="title">XSS攻撃テスト</h1> <div class="forms"> <!-- ラベル --> <label class="label">入力フォーム</label> <div> <!-- コメント入力フォーム --> <input id="input" type="text"/> <!-- 投稿ボタン --> <button id="btn">投稿</button> </div> <!-- コメント表示 --> <label class="label">投稿結果表示</label> <div class="result" id="output"></div> </div> </div> <!-- ボタン実行スクリプト --> <script src="script.js"></script> </body> </html>

上記の様にHTMLを構成しており、コメント入力フォームで文字列を入力して投稿ボタンを押すと、<script>でボタン実行スクリプトを外部ファイルとして呼び出し、入力した文字列をコメント表示の下部に出力するようになっています。

// 投稿ボタン実行スクリプト document.getElementById("btn").onclick = () => { const v = document.getElementById("input").value; document.getElementById("output").innerHTML = v; };

ボタン実行スクリプトは、上記の様にJavaScriptで構成しています。定数vに入力した文字を取得し、innerHTMLを使用してhtmlに出力しています。

検証手順

検証手順としては、入力フォームにXSS攻撃用のDOM要素を文字列で入力して投稿ボタンを押したときに、XSS攻撃が成立するか確認していきます。

検証1:<script>タグを入力してXSS攻撃

まずは、XSS攻撃の教科書的な攻撃手法である<script>タグを入力した攻撃を実行します。

スクリプト入力と検証結果

下記の<script>を交えた文字列を入力します。
alert関数を使用して、「XSS攻撃成功」という文字列を画面上に通知として表示します。

<script>alert('XSS攻撃成功')</script>

しかし、実際に<script>タグを入力した状態で投稿ボタンを押下してみた結果、「XSS攻撃成功」の通知は表示されませんでした。
また、投稿結果表示の下部にも文字列は表示されていません。
スクリプト攻撃例画面

つまり、入力したスクリプトは動いていません

なぜスクリプトが動かないのか

<script>タグが実行されなかったのは、innerHTMLの仕様です。

HTMLのWeb標準仕様を定義しているWHATWG HTML Living Standardの公式ドキュメントには「When inserted using the innerHTML and outerHTML attributes, they do not execute at all.」と記載されています。
日本語訳すると「innerHTML / outerHTML で挿入された <script> は 一切実行されない」ということになります。

そのため、<script>タグを入力する形でのXSS攻撃は、innerHTMLに対しては成功しないということになります。

「だから安全」と言えない理由(別ルートがある)

検証した結果、<script>タグでのXSS攻撃はinnerHTMLは通らないということが分かりましたが、XSS攻撃の手法としては、<script>タグ以外のDOM要素を使用する攻撃手法もあります

次の章では、イベントハンドラ型のXSS攻撃が成立するかについて検証していきます。

検証2:イベントハンドラ型のXSS攻撃

次にイベントハンドラを使用したXSS攻撃がinnerHTMLに通るのか検証していきます。

イベントハンドラ型とは(onerror/onclickなど)

イベントハンドラ型XSSとは、HTML要素に設定できるonclickonerrorなどのイベント属性を悪用し、ユーザー操作やエラー発生をきっかけに JavaScript を実行させる攻撃手法です。

たとえば、次のような入力をinnerHTMLで画面に書き換えた場合を考えます。

<img src="x" onerror="alert(document.cookie)">

画像の読み込みに失敗するとonerrorが発火し、Cookieを取得するJavaScriptが実行されます。<script>タグと違い、イベントハンドラはDOMに追加された時点で有効になるため、innerHTML の使い方次第では攻撃が成立します。

イベントハンドラ型スクリプト入力と検証結果

下記の<img>onerrorのイベントハンドラでalert('XSS攻撃成功')を交えた文字列を入力フォームに入力します。

<img src="x" onerror="alert('XSS攻撃成功')">

入力して投稿ボタンを押下すると、下のように「XSS攻撃成功」が通知として表示されました。
イベントハンドラ型XSS攻撃のデモ画像

つまり、alert関数を使用したXSS攻撃が成功したことになります。

なぜ、イベントハンドラ型XSS攻撃が通るのか

<script>タグは後から追加しても実行されない一方で、onerroronclickといったイベントハンドラは、要素がDOMに追加された時点で有効になります。

この違いにより、innerHTMLの使い方を誤ると、意図せずJavaScriptの実行権限を与えてしまいます。

偽画面作成スクリプトを実行

alertで通知を出すだけでは攻撃した感があまりでないので、次に画面のHTMLを書き換えた偽画面を作成する下記のスクリプトを投稿してみます。

<img src="x" onerror="document.body.innerHTML = '<h1 class=\'title\'>XSS攻撃成功</h1>';">

入力フォームに入力して投稿ボタンを押下すると、入力フォームは消え、下記のように「XSS攻撃成功」という文字列がアプリ画面上に表示されました。
偽画面画像

このように、JavaScriptの実行権限が攻撃者に渡ると、Cookie情報を盗まれてしまったり(※HttpOnly が付いていない場合)、画面改ざんや不正操作を行われたりする危険性がありますので、対策が必要となります。

対策1:innerHTMLを使わない書き換え

innerHTMLに対するイベントハンドラ型XSS攻撃の対策として、innerHTML以外の代わりの関数を使用する対策方法があります。

代わりにtextContent/innerTextを使う

innerHTMLは文字列をHTMLとして解釈してDOMを書き換えるため、ユーザー入力を渡す使い方は非推奨です。代わりに、文字列としてそのまま表示するtextContentinnerTextを使うことで、意図しないJavaScriptの実行を防げます。

たとえば次の違いを見てみます。

// 危険な例 output.innerHTML = userInput; // 安全な代わり output.textContent = userInput; // または output.innerText = userInput;

textContentinnerTextはタグを解釈せず、単なるテキストとして表示するため、onerrorなどのイベントハンドラは有効になりません。

HTMLとして扱うか、文字列として扱うかという書き換えの違いが安全性を大きく左右します。表示目的で値を取得・反映するだけなら、innerHTML を使わず、これらのAPIを選ぶのが基本です。

textContentを使用して検証

textContentを使用すればイベントハンドラ型のXSS攻撃が通らないのか検証するため、下記の様にinnerHTMLの処理をコメントアウトして、textContentに書き換えました。

// 投稿ボタン実行スクリプト document.getElementById("btn").onclick = () => { const v = document.getElementById("input").value; // 処理修正 //document.getElementById("output").innerHTML = v; document.getElementById("output").textContent = v; console.log(document.cookie); };

textContentを使用したソースコードで入力フォームにイベントハンドラ型のスクリプトを入力して投稿ボタンを押下すると、下画像のようにスクリプト内容が文字列としてそのまま表示されました
textContentを使用したデモ画像

このように、textContentを使用することで、スクリプトをHTMLとして読まずにテキストとして読み込むため、スクリプトは実行されずにそのまま文字列として表示されるようになります。

対策2:やむを得ずinnerHTMLを使う場合の防御

ただ、実際のアプリ開発ではやむを得ずinnerHTMLを使わなければならない場面もあると思います。そのような場合は入力チェックや出力時のエスケープ処理、CSPによる許可リストの強化することが重要です。

入力チェック、出力時のエスケープ処理、CSPによる許可リストの強化を行うやり方については、以前別の記事を投稿していますので、下記の記事を参考にしてみてください。

入力チェック・エスケープ処理の参考記事:
クロスサイトスクリプティング(XSS)を図解で理解!攻撃事例と対策を徹底解説
CSPによる許可リストの参考記事:
CSP(Content Security Policy)とは?XSS対策に欠かせない理由と注意点

結論:innerHTMLは“条件付きで危険”だから、設計で対策するべき

本記事の検証から分かる通り、innerHTMLは常に危険というわけではありませんが、使い方を誤るとXSS攻撃が成立する可能性があるAPIです。

<script>タグは実行されなくても、イベントハンドラ型のようにDOM追加時に有効になるJavaScriptの経路は残っています。そのため、ユーザー入力をそのままinnerHTMLで書き換える設計は非推奨です。

表示目的であればtextContentやinnerTextを使う、やむを得ずinnerHTMLを使う場合は入力検証やエスケープ、CSPの設定を組み合わせるなど、設計段階での対策が重要になります。

便利さだけで選ばず、実行文脈に入るかどうかを意識してAPIを使い分けることが、安全なJavaScript開発につながります。