Security
10 min read
2476 views

Beyond alert(1): ReactやVueのSPAsに潜むクロスサイトスクリプティング(XSS)の現実的な危険性 💉

IT
InstaTunnel Team
Published by our engineering team
Beyond alert(1): ReactやVueのSPAsに潜むクロスサイトスクリプティング(XSS)の現実的な危険性 💉

Reactがウェブ開発の主流となった当初、セキュリティ研究者たちは安堵の息をつきました。なぜなら、ユーザー入力を自動的にエスケープするフレームワークが登場したからです。XSSの脆弱性はjQuery時代の遺物となり、レガシーコードや初心者向けチュートリアルに限定されると考えられていました。alert(1)の証明概念も、仮想DOMとコンポーネントベースのアーキテクチャの世界では重要性を失いつつありました。

しかし、その安心感は早計でした。

ReactやVueといった現代のJavaScriptフレームワークは、クロスサイトスクリプティングの脆弱性を排除したわけではありません。むしろ、それらを変容させてしまったのです。これらのフレームワークは堅牢なデフォルト保護を提供しますが、新たな攻撃面やパターンも導入しており、開発者はそれらを誤解しがちです。その結果、XSSの脆弱性がシングルページアプリケーション(SPA)に現れたときに、致命的な侵害へとつながる誤った安心感を生むことがあります。

フレームワーク免疫の迷信は根強い

現代のフレームワークが自動的にXSSを防ぐという考えは、現代ウェブ開発において最も危険な誤解の一つです。ReactはJSXに埋め込まれた値をデフォルトでエスケープし、Vueはインター ポレーテッドコンテンツをサニタイズしますが、これらの保護には明確な境界線があり、実運用のアプリケーションではしばしば越えられています。

実際には、ReactやVueのアプリケーションは、いくつかのよく知られた攻撃ベクトルを通じてXSSに脆弱です。2024年のセキュリティ分析では、これらのフレームワークを用いたアプリケーションにおいてもXSSの脆弱性が依然として存在し、従来よりも深刻な結果をもたらすケースが多いことが示されました。フレームワークのデフォルト保護は、安全だと誤認させる危険な安心感を生み出します。

Reactの意図的に命名されたdangerouslySetInnerHTMLプロパティは、その名の通り危険を示唆しています。この生のHTMLをレンダリングするための抜け穴は、誤用されるとXSSの脆弱性を招きます。危険を示す命名規則にもかかわらず、開発者はユーザー生成コンテンツやマークダウン変換、リッチテキストエディタの出力をレンダリングする際にこのプロパティを頻繁に使用していますが、適切なサニタイズは行われていません。

Vueも同様にv-htmlディレクティブを用いて生のHTMLをレンダリングします。Vueはリスク軽減のためのいくつかの内蔵保護を導入していますが、それでも完全にXSSや類似の脅威から免れるわけではありません。2024年のVue 2のテンプレートコンパイラにおいて発見された脆弱性(CVE-2024-6783)は、フレームワーク自体にもセキュリティ上の欠陥が潜んでいることを示しています。開発者はこれらを積極的に監視し、修正する必要があります。

XSSの進化:反射型から蓄積型DOM操作へ

従来のサーバーレンダリングアプリケーションにおけるXSS攻撃は比較的単純でした。攻撃者はフォーム入力やURLパラメータを通じて悪意のあるJavaScriptを注入し、サーバーはこれをHTMLレスポンスに反映させていました。ブラウザはそのスクリプトを実行し、被害が拡大します。

しかし、SPAはこの状況を根本的に変えました。攻撃はもはやサーバーの反映を必要とせず、完全にDocument Object Model(DOM)内で行われることが多くなっています。DOMベースのXSSは、現代のウェブアプリケーションにおいて最も主要な脅威となり、攻撃はクライアント側のコードに埋め込まれ、ブラウザ内で直接攻撃を仕掛ける手法です。

SPAにおける蓄積型XSSは、持続性とJavaScriptの拡張性を併せ持つため、非常に厄介です。攻撃者が悪意のあるコードを注入し、それがデータベースに保存され、後に他のユーザーにレンダリングされると、その影響は単なるアラートボックスを超えたものになります。

真のターゲット:認証トークンとセッション乗っ取り

現代のSPAにおいて最も破壊的な結果の一つは、認証トークンの漏洩です。JWT(JSON Web Token)の普及と、これらのトークンをlocalStorageに保存する一般的な手法は、資格情報の窃盗を容易にしています。

localStorageは、ユーザビリティのために設計されており、セッション管理の複雑さを避けてデータを永続化できます。しかし、localStorageはページ上のJavaScriptコードからアクセス可能であり、XSS脆弱性を通じて悪意のあるスクリプトに盗まれる危険性があります。

認証トークンがlocalStorageにある場合、成功したXSS攻撃は1行のJavaScriptでこれらを外部に送信できます。攻撃者は暗号化を解読したり認証システムを回避したりする必要はなく、アプリケーションが保存した値を読み取るだけです。トークンが盗まれると、攻撃者は被害者になりすまし、どのデバイスからでもアカウントにアクセスできる状態になります。これにより、被害者がログアウトしてもアクセスを維持される可能性があります。

セキュリティの専門家は、JWTのような敏感な資格情報をlocalStorageに保存しないことを強く推奨しています。代替手段としてhttpOnlyクッキーがあり、これによりJavaScriptからのアクセスを防止できますが、CSRF対策やクロスオリジンの考慮が必要となります。

多くのチュートリアルやボイラープレート、実運用システムがlocalStorageを用いたトークン保存を続けているのは、SPAの認証ロジックが完全にクライアント側で動作しているためです。この設計の便利さは、重大なセキュリティコストを伴います。

静かなる操作:見えない脅威

資格情報の窃盗だけでなく、SPAのXSSはユーザーが気付かないうちに見た目や操作を改ざんすることも可能です。この攻撃は、ユーザーがブラウザのアドレスバーやSSLインジケーターを信頼していることを利用します。

例えばReact SPAで構築された銀行アプリを考えてみてください。攻撃者が蓄積型XSSを仕掛けると(脆弱なユーザーネーム表示やコメントシステムを通じて)、DOMを操作して不正な残高表示や偽の送金確認、取引履歴の改ざんを行えます。被害者は、正規のアプリケーションデータと見分けがつかない状態で情報を受け取り、操作されていることに気付かないままです。

これは単なるウェブページの改ざんを超えています。高度な攻撃者はAPIレスポンスを傍受・改ざんし、被害者のブラウザ内で中間者攻撃を行うことも可能です。正規のAPI呼び出しを行った際に、悪意のあるコードがレスポンスを操作し、その結果をReactやVueに渡してレンダリングさせるのです。

被害者は、アプリのデザインや挙動に完全に一致した偽情報を目にし、何かがおかしいと気付くのは、実際の被害—未払い、未承認の送金、個人情報の漏洩—が起きてからです。

APIの悪用:ユーザの代理として行動

現代のSPAは、バックエンドと通信するAPIクライアントとして機能します。この構造は、XSS脆弱性のもう一つの深刻な攻撃ベクトルを生み出します。それは、被害者のブラウザから不正なAPI呼び出しを行うことです。

認証済みのユーザのセッション内で悪意のあるJavaScriptが実行されると、そのユーザの権限とアクセス権をすべて引き継ぎます。注入されたコードは、正規のユーザが行うのと同じようにAPI呼び出しを行えます。これには、クッキーやlocalStorageに保存された認証トークン、アプリが生成したCSRFトークンも含まれ、ユーザのIPアドレスから発信されます。

つまり、XSS脆弱性は、攻撃者にユーザの権限で資金移動やパスワード変更、設定変更、データ削除、コンテンツ投稿、制限された情報へのアクセスを許すことになります。これらのリクエストは、サーバ側から見れば正規のユーザ操作と区別がつきません。

攻撃者はバックエンドのセキュリティ対策を理解したり突破したりする必要はありません。暗号化を解読したりトークンを偽造したりする必要もありません。単に、既存の有効な認証情報を利用して、不正な操作を実行するだけです。バックエンドは、正規の認証済みリクエストとみなして処理しますが、そのリクエストが実際にユーザによるものかどうかを見分ける術はありません。

この攻撃ベクトルは、金融取引や重要な操作を行うアプリケーションにおいて特に危険です。バックエンドが、重要な操作に対して追加の検証(例:資金移動には再認証、アカウント変更にはメール確認)を行わない場合、XSSはユーザの知らないうちにこれらの操作を実行させる直接的な手段となります。

現代SPAにおける一般的な脆弱パターン

ReactやVueアプリケーションにおいて、フレームワークのデフォルト保護にもかかわらずXSS脆弱性を生み出す繰り返しのパターンがあります:

ユーザ生成のリッチコンテンツ:コメントやプロフィールの自己紹介、共同編集ツールなど、フォーマットされたコンテンツを作成できるアプリは、しばしばdangerouslySetInnerHTMLv-htmlを使ってレンダリングし、十分なサニタイズを行っていません。

サードパーティコンテンツの統合:外部ソースからのコンテンツ(ソーシャルメディア埋め込み、ユーザ投稿のURL、サードパーティAPIのレスポンス)を信頼しすぎると、悪意のあるスクリプトが埋め込まれる危険性があります。

サーバーサイドレンダリングとの競合:サーバーサイドレンダリングとクライアント側のハイドレーションを併用する場合、どちらの層がサニタイズを担当しているか混乱を招くことがあります。安全にレンダリングされたデータも、クライアント側で再ハイドレートされる際に危険となる場合があります。

動的コンポーネントレンダリング:データに基づいて動的にコンポーネントをレンダリングする場合、攻撃者がコンポーネント名やpropsに仕込むことで、フレームワークが適切にエスケープしないXSSを誘発する可能性があります。

URLやルートパラメータのインジェクション:URLパラメータやルートセグメントを適切にエンコードせずにUIに表示すると、反射型XSSの脆弱性が生じることがあります。URL自体はサーバ反映を引き起こしませんが、クライアント側のルーターがパラメータを読み取り、そのままDOMにレンダリングする場合です。

深層防御:フレームワークのデフォルトを超えて

SPAをXSSから守るには、多層的なセキュリティ対策が必要です。フレームワークのデフォルトだけに頼らず、次のような対策を講じる必要があります:

Content Security Policy (CSP):インラインスクリプトを禁止し、スクリプトソースを制限する厳格なCSPを導入することで、XSSの悪用を防ぎます。攻撃者が悪意のあるコードを注入しても、適切に設定されたCSPは実行を阻止します。

入力のサニタイズ:ユーザ生成コンテンツは、DOMPurifyなどの信頼できるライブラリを使ってサニタイズし、dangerouslySetInnerHTMLv-htmlに渡す前に適用します。レンダリング直前にサニタイズを行うことで、未処理のコンテンツの流通を防ぎます。

安全なトークン保存:認証トークンは可能な限りhttpOnlyかつセキュアなクッキーに保存すべきです。localStorageを使う必要がある場合は、トークンのローテーションや短期有効期限、異常アクセスの監視など追加の保護策を講じます。

出力のエンコーディング:フレームワークの自動エスケープ機能が正しく適用されているか検証し、URLパラメータやJSONデータ、APIレスポンスは潜在的に危険とみなして適切にエンコードします。

定期的なセキュリティ監査:自動化ツールを使ってdangerouslySetInnerHTMLの未サニタイズ使用などの危険なパターンを検出し、手動のコードレビューでユーザ入力の流れとレンダリング箇所を重点的に確認します。

フレームワークのアップデート:Vue 2の脆弱性(2024年発見)など、フレームワーク自体のセキュリティ脆弱性は定期的なアップデートで対処します。最新のセキュリティパッチを適用しましょう。

結論:XSSは解決済みの問題ではない

従来のウェブアプリからSPAへの移行は、XSSを根絶したわけではありません。むしろ、より危険な形態に変容させてしまったのです。現代のフレームワークは優れたデフォルト保護を提供しますが、それを回避しようとする開発者の意図や誤解を完全に防ぐことはできません。

alert(1)の証明概念は、これらの脆弱性がもたらす真のリスクを軽視させるものでしかありません。実運用のSPAでは、XSSは資格情報の窃盗、ユーザーインターフェースの静かな改ざん、APIの不正操作を可能にし、すべてがユーザのブラウザ内で見えない形で行われます。

ユーザ生成コンテンツのレンダリングやサードパーティデータの統合、クライアント側に保存された敏感な資格情報がある限り、XSSは重要なセキュリティ課題であり続けます。私たちが使うフレームワークは、強力なツールであり高度な保護を提供しますが、魔法の盾ではありません。

開発者はReactやVueの使い方だけでなく、安全に使う方法を理解しなければなりません。フレームワークの保護の境界を認識し、多層的な防御策を実装し、外部データを潜在的に危険なものとみなすことが必要です。こうしたセキュリティ意識を持った開発だけが、フレームワーク免疫の誤った約束を超え、ユーザをXSS攻撃から守るSPAを構築できるのです。

次回コードレビューでdangerouslySetInnerHTMLを見かけたら、その名前の不吉さだけでなく、どのようにサニタイズしているのか、データの出所はどこか、攻撃者が悪意を持った場合に何ができるかを問いましょう。現代のSPAの世界では、それらの問いが、あなたのユーザとシステムを破滅から守る唯一の鍵となるかもしれません。

Continue from this article into the most relevant product guides and workflows.

Related Topics

#cross-site scripting, XSS vulnerabilities, SPA security, React security, Vue security, XSS attacks, web application security, DOM-based XSS, stored XSS, JavaScript security, dangerouslySetInnerHTML, v-html directive, JWT token theft, localStorage security, authentication token security, session hijacking, XSS prevention, React XSS, Vue XSS, modern web security, Content Security Policy, CSP implementation, DOMPurify, input sanitization, output encoding, httpOnly cookies, CSRF protection, XSS mitigation, security vulnerabilities, web security best practices, React vulnerabilities, Vue vulnerabilities, single page application security, client-side security, frontend security, JavaScript framework security, virtual DOM security, component-based architecture security, DOM manipulation attacks, API exploitation, credential theft, token exfiltration, silent attacks, unauthorized API calls, user impersonation, session token theft, XSS defense, secure coding practices, web security audit, vulnerability assessment, security hardening, secure token storage, sanitization libraries, security headers, defense in depth, secure SPA development, web security 2025, cybersecurity, application security, OWASP, security compliance, penetration testing, vulnerability management, secure development lifecycle

Keep building with InstaTunnel

Read the docs for implementation details or compare plans before you ship.

Share this article

More InstaTunnel Insights

Discover more tutorials, tips, and updates to help you build better with localhost tunneling.

Browse All Articles