Angularの「自動サニタイズ」を過信するな:XSSの入り口を塞ぐ実務的アプローチ
現場でコードレビューをしていると、必ずと言っていいほど耳にする言葉がある。「Angularを使っているからXSS(クロスサイトスクリプティング)は大丈夫ですよね?」。
結論から言おう。それは半分正解で、半分は致命的な勘違いだ。
Angularの強力な武器である「自動サニタイズ」は、確かにフレームワーク側で提供するテンプレートバインディングにおいては非常に優秀な防壁だ。しかし、攻撃者はその「防壁の隙間」を嗅ぎ分けるプロフェッショナルである。今回は、Angularの防御機構の裏側を覗き、なぜ開発者が「意図的に」脆弱性を生み出してしまうのか、そのメカニズムと完全な防御策を共有する。
—
1. 自動サニタイズの限界と「信頼された値」の罠
Angularの DomSanitizer は、テンプレートに値を挿入する際、その値が「安全なコンテキスト(HTML, Style, URLなど)」に含まれているかを確認し、危険な文字列(タグやjavascript:プロトコルなど)を無害化する。
しかし、現場でよくあるのは「デザイン要件でどうしてもHTMLをそのまま流し込みたい」というケースだ。ここで多くのエンジニアが bypassSecurityTrustHtml のような危険なメソッドに手を出してしまう。
攻撃シナリオ:なぜ bypassSecurityTrust が危ないのか
攻撃者は、あなたが「安全だ」と信じて許可した入力欄を狙う。例えば、ユーザープロフィールのHTML編集機能を作ったとする。
// 悪い例:安易にサニタイズをバイパスしてはいけない this.trustedHtml = this.sanitizer.bypassSecurityTrustHtml(userProvidedInput);
もし userProvidedInput に以下のようなペイロードが含まれていたらどうなるか。
Angularは bypassSecurityTrustHtml を通じて「これは信頼されたHTMLだ」と認識し、サニタイズをスキップする。結果、ブラウザは即座にJavaScriptを実行する。これがXSSの最短ルートだ。
---
2. 安全な実装のための「3つの鉄則」
実務でXSSを完全に排除するには、以下のルールを徹底してほしい。
1. bypassSecurityTrust系メソッドを禁止する:
よほどの理由がない限り、ESLintのルールでこれらのメソッド使用を警告レベルに設定せよ。
2. テンプレート内での直接挿入を避ける:
データは可能な限り {{ }}(補間)で扱い、HTML構造自体を動的に変更する際は ngIf や ngFor を使う。
3. CSP(コンテンツセキュリティポリシー)で多層防御を敷く:
アプリ側のミスをカバーするために、HTTPヘッダーでブラウザに「何を実行させて良いか」を強制する。
---
3. 実践:セキュアな実装とインフラ設定
実装例:どうしてもHTMLをレンダリングする必要がある場合
もしサードパーティのテキストエディタなどでHTMLを扱う必要があるなら、bypassSecurityTrust は使わず、DOMPurify のような信頼できるライブラリを挟むのが正解だ。
import as DOMPurify from 'dompurify';
// サーバーから受け取ったHTMLをDOMPurifyで掃除してから表示する
const rawHtml = 'Hello';
const cleanHtml = DOMPurify.sanitize(rawHtml);
// これならXSSを無効化しつつ、ボールドタグなどは維持できる this.safeHtml = this.sanitizer.bypassSecurityTrustHtml(cleanHtml);
インフラ設定:NginxによるCSP(Content-Security-Policy)の強制
アプリ側で万が一XSSが混入しても、CSPがブラウザ側で実行を阻止する。以下の設定は、インラインスクリプトの実行を原則禁止する強力なものだ。
Nginx設定ファイルに追加 add_header Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'; base-uri 'self';";
default-src 'self': 外部からの不正なスクリプト読み込みを遮断。script-src 'self': インラインスクリプト(等)の実行をブロック。object-src 'none': Flash等の古いプラグインによる攻撃を防止。
---
セキュリティチーフからのアドバイス
「動くものを作る」のはエンジニアの最低条件だが、「壊されないものを作る」のがプロの仕事だ。
Angularのサニタイズ機能は強力なツールだが、決して万能ではない。特に、バックエンドからのデータ入力や、動的なDOM操作が絡む部分には必ず「疑いの目」を持ってほしい。セキュリティは機能開発の最後に行うものではなく、設計段階からコードの端々に埋め込むものだ。
もしチーム内で「このbypassメソッド、使っても大丈夫?」という議論が起きたら、まずは「それが本当に必要か? 代替手段はないか?」を徹底的に問うてみてほしい。その泥臭い議論こそが、インシデントゼロへの近道になるはずだ。
コメント