XSS対策におけるCSPとTrusted Types APIの役割
はじめに
あけましてよろしくお願いします。2025年もよろしくお願いします。 沖縄でエンジニアをしています
最近は、Vue / Nuxt 3 でのフロントエンド開発に注力しており、 特にセキュリティに強い関心を持っています。今回は、XSS(クロスサイトスクリプティング)の脅威からアプリケーションを保護するための実践的な対策について共有します。
XSSとは?
XSS(クロスサイトスクリプティング)は、 Webアプリケーションにおける最も一般的なセキュリティ脆弱性の一つです。
攻撃者は悪意のあるスクリプトをWebサイトに注入し、 ユーザーのブラウザ上で実行させることで、様々な被害を引き起こします。
このようなXSSの脅威からWebサイトを保護するために、CSP(Content Security Policy)やTrusted Types APIは重要な役割を果たします。
Content Security Policy (CSP)
CSPは、Webサイトが読み込むことができるリソース(スクリプト、スタイルシート、画像など)を制限することで、XSS攻撃を軽減する仕組みです。 CSPを設定することで、攻撃者が注入したスクリプトの実行を阻止し、Webサイトを保護することができます。
- 主な機能
- リソースの許可:
script-srcディレクティブを使って、JavaScriptの実行を許可するドメインを制限します。これにより、悪意のある外部スクリプトの読み込みを防止できます。 - インラインスクリプトの制限:
unsafe-inlineを使用しない限り、インラインスクリプトの実行をデフォルトで禁止します。 これにより、HTMLに直接記述された悪意のあるスクリプトの実行を阻止します。 strict-dynamicとnonce:strict-dynamicキーワードとnonceを使用することで、インラインスクリプトを安全に実行できます。nonceは、サーバー側で暗号化トークンが生成され、HTTPレスポンスヘッダーとHTML内の<script>タグの両方に含まれます。 トークンが一致することによって初めて実行することができるので、許可されたスクリプトのみが実行されることを保証します。
- リソースの許可:
他にもセキュリティインシデントを早期発見することができるレポート機能や設定したCSPがどのように動作するかなどをテストすることもできます
DOM-based XSS: JavaScriptを使って動的にHTMLを生成する際に発生します。
Trusted Types API
Trusted Types APIは、DOMベースのXSS攻撃に対する強力な防御策です。このAPIは、HTMLに反映させる情報をポリシーと呼ばれる関数で検査し、安全な型に変換することで、悪意のあるスクリプトの実行を防ぎます。
- 主な機能:
- DOM操作の安全化:
innerHTMLなどのDOM操作を行う際に、TrustedHTMLなどの安全な型を使用することを強制します。これにより、信頼できないソースからのHTML文字列がDOMに挿入されることを防ぎます。 - ポリシーの設定: ポリシーを使用して、どのような変換が安全であるかを定義できます。これにより、特定の要件に合わせてセキュリティポリシーをカスタマイズできます。
- 既存のコードへの導入: Trusted Types APIは、既存のコードに段階的に導入できます。最初はレポートモードで動作させ、徐々に強制モードに移行することが推奨されます。
- DOM操作の安全化:
CSPとTrusted Types APIの組み合わせ
CSPとTrusted Types APIは、それぞれ異なるアプローチでXSS攻撃に対処しますが、両方を組み合わせることで、より強力な防御層を構築できます。
- CSP は、リソースの読み込みを制御し、インラインスクリプトを制限することで、XSS攻撃の侵入を防ぎます。
- Trusted Types API は、DOM操作を安全化し、悪意のあるスクリプトの実行を阻止します。
両方の技術を組み合わせることで、攻撃者がCSPを回避した場合でも、Trusted Types APIによってDOM操作が安全に保たれ、XSS攻撃のリスクを大幅に軽減できます。
まとめ
XSS対策には、CSPとTrusted Types APIの両方を活用することが重要です。
CSPはリソースの読み込みを制御し、Trusted Types APIはDOM操作を安全化することで、多層防御を実現できます。 これらの技術を適切に利用することで、XSS攻撃のリスクを大幅に減らすことができます。
参考資料
TypeScriptを名前に対して型をつける
TSの型システムは名前に型がつかない
どうも、沖縄でエンジニアをしているものです 今回はTypeScriptの型システムについてと、名前的型付けについて軽く書きます
TypeScriptの型システムでは「名前的型付け」を採用しており、 そもそもTypeScript では「構造的型付け (structural typing)」が採用されています
「構造的型付け」とは?
要はその型が持っているプロパティやメソッドなど、 構造に着目してその型の互換性の判定などを行います
class Person { walk() {} } class Dog { walk() {} } const person: Person = new Dog() // 構造が同じなので、 // コンパイルエラーにならない
「構造的型付け」以外でも「名前的型付け (nominal typing)」というものもあります
「名前的型付け」とは?
名前的型付けは、文字通りその型の'名前'に着目して型の互換性を判定します JavaやC#などに用いられている型システムになります
class Person { public void walk() {} } class Dog { public void walk() {} } Person person = new Person(); Dog dog = person; // コンパイルエラー: 不適合な型
じゃあ、TSでは「名前的型付け」ができない?
「Blanded Types」という実装パターンで名前的型付けを再現することができます
string & { __brand: 'Raw' } にて交差型(intersection type)を用いることで
ただstringだけで値は代入できませんが、指定の型をアサーションすることでstringとしても扱えるかつ、名前で型を付与することができます
type Raw = string & { __bland: 'Raw' } // const raw: Raw = 'raw' // Type 'string' is not assignable to type 'Raw'. // Type 'string' is not assignable to type '{ __bland: "Raw"; }'.(2322) const raw: Raw = 'raw' as Raw
ジェネリクスでもBlanded Typesを活用することもできます
declare const __bland: unique symbol type Brand<B> = { [__bland]: B } type Blanded<T, B> = T & Brand<B> type Raw = Blanded<string, "Raw ">
おわりに
TypeScriptは「構造的型付け」を採用しているので、実装パターンで「名前的型付け」を再現できるのは初めて知りました
はてなでもブログを始めてみました
どうも、はじめまして
沖縄でWebエンジニアをしているrikuです
普段はQiitaで技術ブログを書いているのですが、 ある程度しっかりした内容であったり長い文章を書かないといけません
また、技術に関することも書かねばならない (自分でそう思っているだけ)
ここ最近はアウトプットすることの重要性なども感じるようになり得た知識をどこか気軽にアウトプットできるところないかなと探していた所はてなブログにしました
ん?某「X」でもいいじゃないかって?
あんな治安が悪いところに技術だの思っていることだの書けばフルボッコにあうだけなのでやめときます
ので、とりあえず技術的に少しアウトプットしたいことや書きたいことを書いていきます!
よろしくお願いしゃぁぁぁす!
