【入門編】Trusted Types APIによるDOMベースXSSの根本的防御 – アプリケーションセキュリティ & 安全な開発防御ガイド

あなたのWebサイトが「鍵の壊れた家」にならないために。Trusted Typesで防ぐDOMベースXSSの仕組み

こんにちは。現場で泥臭くコードと格闘し、時にインシデントの火消しに走り回っているエンジニアの皆さん、お疲れ様です。

今日は、Webアプリケーションにおける「玄関の鍵」の話をしましょう。特に、最近のWeb開発で避けては通れない「DOMベースXSS(クロスサイトスクリプティング)」と、それを根本から封じ込める強力な武器「Trusted Types API」について解説します。

難しい理論は置いておいて、まずは身近な例えから入っていきましょう。

—

1. なぜ「innerHTML」は泥棒を招くのか?

家を想像してください。あなたは玄関に「郵便受け」を設置しました。ここから手紙(データ)が入ってくるのは便利ですよね。

しかし、もし誰かが郵便受けから「家の中の壁に勝手に落書きをするスプレー」を差し込んできたらどうでしょう? 悪意のある誰かが、あなたの家の壁に「火をつけろ」「全財産を盗め」という指示(JavaScriptのコード)を書き込んだら、家は一瞬で乗っ取られてしまいます。

Webの世界における innerHTML は、まさにこの「郵便受けから壁に直接スプレーを吹きかけるようなもの」です。

// 危険!ユーザーが入力した名前をそのままinnerHTMLに流し込む例
const name = location.hash.substring(1); // URLのハッシュから名前を取得
document.getElementById(‘welcome’).innerHTML = “こんにちは、” + name + “さん!”;

もし攻撃者がURLを細工して、name の場所に のようなコードを紛れ込ませたら? ブラウザはそれを「ただの文字列」ではなく「実行すべき命令」として解釈し、即座に悪意のある動作を実行してしまいます。これがDOMベースXSSの正体です。

—

2. Trusted Types:家の中に「番人」を置く

これまでの対策は、入力されるたびに「この手紙は怪しくないか?」とチェックするようなものでした(サニタイズ)。でも、人間がチェックするのは疲れるし、どうしても見落としが発生します。

そこで登場するのが Trusted Types です。

これは、家の中に「厳格な番人」を雇うようなものです。番人は、たとえ郵便受けから届いたものであっても、「許可された安全なルートを通ったもの以外、絶対に壁(innerHTMLなど)には触れさせない」というルールを徹底します。

番人が働くための準備(ヘッダーの設定)

まずは、Webサーバーからブラウザに向けて「これからは番人を働かせるよ!」という宣言を送ります。HTTPヘッダーに以下を設定するだけです。

サーバーの設定(ApacheやNginxなど)
Content-Security-Policy: require-trusted-types-for ‘script’;

このヘッダーをつけた瞬間、ブラウザは「innerHTML や eval() に生の文字列を渡すとエラーを吐く」という厳格モードに切り替わります。これで、不用意な文字列挿入はすべて門前払いされるようになるんです。

—

3. 実装のステップ:番人に「安全なパス」を教える

番人を雇うと、今度は「本当に安全なデータ」までブロックされてしまいます。そこで、開発者は「これは安全だよ」という証(ポリシー)を番人に教えてあげる必要があります。

実装例:ポリシーの作成

// 「default」という名前のポリシーを作成
const policy = trustedTypes.createPolicy(‘default’, {
createHTML: (input) => {
// ここで危険なタグを取り除くなどの処理を行う
// 本来はDOMPurifyなどの信頼できるライブラリを使うのが鉄則です!
return DOMPurify.sanitize(input);
}
});

// これ以降、innerHTMLに文字列を渡しても、
// 自動的に「default」ポリシーが適用されて安全になる!
document.getElementById(‘welcome’).innerHTML = “こんにちは、ユーザーさん!”;

このように、一度ポリシーを作ってしまえば、コード全体で「安全な加工」が強制されます。開発者がうっかり innerHTML に生の文字列を渡そうとしても、ブラウザが「おい!そのデータはまだ番人のチェックを受けていないぞ!」と即座に警告(例外)を出して止めてくれるのです。

—

4. なぜこれが「最強の防御」なのか

多くのセキュリティ対策は「攻撃を防ぐ」ことに必死になりがちですが、Trusted Typesは「そもそも危険なメソッドを使わせない」というアプローチをとっています。

  • 開発ミスを防げる: 疲れていてサニタイズを忘れても、ブラウザが止めてくれる。
  • 根本解決: 「怪しい入力を探す」のではなく「危険な穴を塞ぐ」という設計思想。
  • モダンな標準機能: 追加のライブラリを大量に入れなくても、ブラウザ(ChromeやEdgeなど)が標準でサポートしている。

—

まとめ:一歩ずつ、安全な家を作っていこう

セキュリティは「完璧」を目指すと苦しくなりますが、「仕組みで防ぐ」ことを覚えると非常に楽になります。

1. まずはヘッダーを導入して、どこが「危険な穴」になっているか把握する。(エラーログが出るので一目瞭然です)
2. 重要な箇所から順にポリシーを作成し、番人を配置していく。

いきなり全てを変える必要はありません。まずは小さく試して、あなたのアプリケーションという「家」を、少しずつ強固な要塞に変えていきましょう。

何か分からないことがあれば、いつでもまた聞きに来てくださいね。一歩ずつ、着実に安全な開発環境を築いていきましょう!

コメント

タイトルとURLをコピーしました