レガシーの呪縛:AngularJSサンドボックス回避と、現代のフロントエンドに潜む「動的評価」の罠
現場でインシデント対応をしていると、いまだに「数年前の負債」という名のAngularJS(1.x系)アプリケーションに出くわすことがある。当時の開発者は「フレームワークが自動でエスケープしてくれるから安心だ」と信じて疑わなかっただろう。だが、セキュリティの世界に「絶対」はない。
今日は、多くのエンジニアが「魔法」だと思っているテンプレートエンジンの裏側、特にAngularJSのサンドボックスがなぜ崩壊し、現代のフレームワークでもなぜ「動的評価」が危険なのかを、泥臭い実務の視点から紐解いていく。
—
1. AngularJSのサンドボックスは「箱」ですらなかった
AngularJS 1.x系において、{{ ... }} という構文は単なる表示用ではない。ブラウザ側でJavaScriptのサブセットを評価するエンジンが動いている。脆弱性の核心は、この評価エンジンに「サンドボックス」を設けて、危険な関数(windowやconstructorなど)へのアクセスを制限しようとした点にある。
しかし、攻撃者はこの制限を「バイパスするパズル」として楽しんだ。有名なペイロードを見てみよう。
PoC:サンドボックス回避の典型例
// 古いAngularJS環境で実行可能な悪意ある式
{{
constructor.constructor(‘alert(document.domain)’)()
}}
この式がなぜ通るのか? constructor プロパティを辿ることで、JavaScriptの Function コンストラクタに到達し、サンドボックスの外側で任意のJSコードを実行できてしまうからだ。開発者が「ユーザー入力値をテンプレートにそのまま埋め込んだ」瞬間、この悪夢が始まる。WAFで alert を弾いたところで、eval や String.fromCharCode を駆使すればいくらでも難読化される。
—
2. 「自動エスケープ」の限界と現代の脆弱性
ReactやVueといった現代のフレームワークは、デフォルトでDOMエスケープを行うため、単純なXSSは劇的に減った。しかし、インシデント現場で見かけるのは「自動エスケープを意図的に無効化した箇所」の脆弱性だ。
dangerouslySetInnerHTML(React)v-html(Vue)ng-bind-html(AngularJS)
これらは「開発者が安全性を保証する」という前提で用意されたバックドアだ。バックエンドから返ってきた信頼できないHTMLをここに流し込めば、フレームワークの防御壁は一瞬で無効化される。
—
3. 実践:セキュアな設計と実装の防波堤
では、我々はどう戦うべきか。結論はシンプルで、「テンプレートエンジンに動的なコード評価をさせない」ことと「出力時のコンテキストに応じたサニタイズ」の二段構えだ。
防御策A:CSP(Content Security Policy)による封じ込め
JavaScriptのインライン実行を禁止する。これが現代のXSS対策における最強の防波堤だ。
Nginx設定例:
信頼できるドメイン以外のスクリプト実行を禁止
‘unsafe-eval’ を含めないことがAngularJS対策の要
add_header Content-Security-Policy “default-src ‘self’; script-src ‘self’ https://trusted.cdn.com; object-src ‘none’;”;
防御策B:DOMPurifyによるHTMLサニタイズ
どうしてもHTMLをレンダリングする必要がある場合は、フレームワーク任せにせず、信頼性の高いライブラリでクリーニングする。
JavaScript 実装サンプル:
import DOMPurify from ‘dompurify’;
// ユーザー入力を受け取った直後にサニタイズする
const untrustedInput = ““;
const cleanHTML = DOMPurify.sanitize(untrustedInput);
// 安全なHTMLのみをDOMに挿入
document.getElementById(‘content’).innerHTML = cleanHTML;
—
4. 最後に:セキュリティは「疑う」ことから始まる
私が駆け出しの頃、先輩に言われた言葉がある。「フレームワークのドキュメントを読め。そして、そのフレームワークがどうやって『あなたのコード』を操作しているか、その裏側の挙動を疑え」。
AngularJSのテンプレートインジェクションは、まさに「便利な機能が、いかに攻撃の踏み台にされやすいか」を象徴する事例だ。最新のフレームワークを使っていても、ロジックを組む際に「もしこの変数が攻撃者に制御されていたら、ブラウザはどう解釈するか?」と一歩立ち止まって考えてほしい。
もし今、手元にレガシーなAngularJSアプリがあるなら、まずはCSPを厳格化し、ng-bind-html を使っている箇所を全検索することから始めてくれ。それが、大規模なデータ漏洩を防ぐための、最も泥臭く、そして最も確実な第一歩だ。
何か技術的な壁にぶつかったら、またいつでも聞いてくれ。現場の最前線で戦う君たちを、私は常にサポートしている。
コメント