SVGのセキュリティリスクとサニタイズの考え方:XSS経路を閉じる安全な掲載手順

SVGファイルは拡張子こそ画像ですが、中身はXMLベースのDOMツリーです。<script> を書くことも、onclick のようなイベント属性を使うこともできます。そのため、ユーザー投稿や外部から受け取ったSVGをそのまま innerHTML に設定したり、HTMLにインライン展開したりすると XSS(クロスサイトスクリプティング)の経路になります

FigmaやIllustratorから書き出されたSVGにも、エディタ固有のメタデータや不要属性が大量に含まれており、セキュリティ面だけでなくファイルサイズの観点でも後処理が必要です。

この記事では、SVGに潜むセキュリティリスクと、掲載方法ごとのリスクの違い、そして安全に掲載するための手順を整理します。

なぜSVGは「信頼できない入力」として扱う必要があるのか

画像のようで実態はDOM

SVGはXMLで書かれたDOMツリーです。HTMLに <svg>...</svg> として埋め込むと、ブラウザはHTMLと同様にパースして スクリプト実行まで許可します

HTML
<!-- これはブラウザで実行される -->
<svg xmlns="http://www.w3.org/2000/svg">
  <script>alert(document.cookie)</script>
</svg>

<img src="file.svg"> で読み込んだ場合は基本的にスクリプトは実行されませんが、インライン展開(HTMLに直接 <svg> を書き込む形)の場合は実行経路が開きます。

「誰が作ったか」ではなく「サニタイズを経由したか」

ポイントは「信頼できる人から受け取ったSVGだから安全」ではありません。サニタイズ処理を経由しているかどうかが判断基準です。デザイナー納品のファイルでも、以下の経路で危険なコードが混入する可能性があります。

  • コピー元のサイトに仕込まれていた
  • Illustratorのプラグインが追加したメタデータ
  • 古いエディタが残したXLink参照

外部から入ってくるSVGは一律サニタイズする運用が、最もミスが起きにくい方法です。

除去すべき要素と属性

必ず除去すべき要素

以下のタグは 問答無用で削除します。

タグ削除する理由
<script>JavaScript実行の直接経路
<style>CSS内の url(javascript:...) やセレクタ経由の攻撃
<metadata>機密情報が混ざりうる、かつ表示には不要
<foreignObject>任意のHTML / JSを埋め込める
<iframe>SVG内で別ドキュメントを読み込む

必ず除去すべき属性

属性削除する理由
on*onclick onload など)イベントハンドラ経由でJSが実行される
href / xlink:href外部URL参照<use> <image> 要素で外部リソースを読み込む攻撃の経路
href / xlink:href / srcjavascript: data: vbscript: スキームURIスキーム経由のJS実行
style 属性内の url(...)外部リソース参照、url(javascript:) の危険
エディタ独自の名前空間(inkscape: sodipodi: sketch: など)安全だが表示に不要

ここで注意したいのが xlink:href の扱いです。内部参照(#id 形式)は安全として残す必要があります。グラデーションや <symbol> の参照で使われるため、これを消すとレンダリングが壊れます。除去するのは https://... のような外部参照だけです。

任意で除去する属性

属性判断基準
id / class1ページに複数のSVGを埋め込む場合、ID衝突を防ぐために落とす
style 属性CSSで統一管理したい場合に落とす
<title> / <desc>アクセシビリティ観点では残す方が良い。冗長なら落とす

記事投稿時は id / classstyle を落とすのが無難、単独アイコン配置時は全部残す、といった使い分けになります。

掲載方法ごとのリスク

<img src="file.svg">

  • スクリプト実行: しない(<img> で読み込まれたSVGは基本的にJSが実行されない)
  • リスク: 低
  • 注意: 画像として表示されるだけなので、SVG固有の機能(CSSでの色変更、ホバー時のアニメーションなど)は使えない

background-image: url(file.svg)

  • スクリプト実行: しない
  • リスク: 低
  • 注意: fill="currentColor" のような動的な色変更が使えない

HTMLに直接インライン展開

  • スクリプト実行: する
  • リスク: 高(サニタイズ必須)
  • メリット: CSS / JSから操作できる、currentColor でテーマ色に追従する

ユーザー投稿や外部納品のSVGをインライン展開する場合は、必ずサニタイズを挟むのが原則です。

WordPressのメディアライブラリにアップロード

WordPressはデフォルトでSVGアップロードを無効にしています。プラグインや functions.php でSVGアップロードを許可する場合は、アップロード時点でサニタイズ処理を挟むプラグイン(Safe SVGなど)を併用するのが推奨です。アップロード時にサニタイズしていないと、<img> 経由の表示でも管理画面プレビューで実行経路が開くことがあります。

デザインツール書き出しに含まれる不要属性

Figma / Illustrator / Sketchから書き出したSVGには、表示に関係ないメタデータが大量に含まれます。セキュリティリスクだけでなくファイルサイズの観点でも、後処理が必要です。

Illustrator

HTML
<svg xmlns:inkscape="..." xmlns:sodipodi="..."
  inkscape:version="1.0" sodipodi:docname="icon.svg">
  <!-- 本体 -->
</svg>

inkscape: sodipodi: 名前空間のメタ情報で、ファイルサイズを増やす原因になります。削除推奨です。

Figma

HTML
<svg>
  <metadata>Created by Figma</metadata>
  <defs>
    <clipPath id="clip0_1_2">...</clipPath>
  </defs>
  <g clip-path="url(#clip0_1_2)">...</g>
</svg>
  • <metadata> は常に削除
  • clip0_1_2 のような自動生成IDは、同じページに複数アイコンを配置するとID衝突するので、記事投稿時にはIDを除去またはリネームしておく

Sketch

HTML
<svg xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
  <title>icon</title>
  <g sketch:type="MSLayerGroup">...</g>
</svg>

sketch: 名前空間は削除対象です。<title> はツールチップ表示に使われるので、アクセシビリティ観点では残すか削除するかをケースバイケースで判断します。

CSPによる多層防御

サニタイズを忘れても、Content Security Policy(CSP)で被害を最小化できます。サニタイズの代わりではなく、「サニタイズが漏れた場合の最後の壁」として入れておきます。

最低限のCSPヘッダー

Content-Security-Policy:
  default-src 'self';
  script-src 'self';
  style-src 'self' 'unsafe-inline';
  img-src 'self' data:;
  • script-src 'self' により、SVG内の <script> が仮に実行されそうになっても 外部ドメインへのリクエストが遮断される
  • script-src'unsafe-inline'絶対に含めない

SVG特有のCSP注意点

  • <use href="https://evil.example.com/..."> のような外部参照は connect-src で制限する
  • インラインSVG内の <style> を使う場合は style-src 'unsafe-inline' が必要になることがある(その場合はスタイルの外出しを検討)

掲載前チェックリスト

SVGを記事やページに貼り付ける前に確認する項目です。

  • <script> <style> <foreignObject> <iframe> <metadata> が入っていない
  • on* イベント属性が残っていない
  • xlink:href / href外部URLを指していない
  • javascript: data: vbscript: スキームが含まれていない
  • 記事内で同じ id が使われていない(Figmaのクリップパス自動ID等)
  • <title> <desc> をアクセシビリティ観点で残すか判断した
  • CSPヘッダーで script-src 'unsafe-inline' が入っていない

まとめ

SVGは「画像だから安全」ではなく、HTMLと同じDOMとして扱われる前提で運用する必要があります。安全に掲載するには以下の4点です。

  • ユーザー投稿・外部納品SVGは必ずサニタイズしてから掲載する
  • 掲載方法によってリスクが変わる(<img> は低、インライン展開は高)
  • FigmaやIllustratorの書き出しは不要属性も多く、サイズ削減の観点でもクリーニングが必要
  • サニタイズ漏れに備えて CSPで多層防御を行う

サニタイズをコードで自前実装するなら、DOMParserで解析してホワイトリストベースで要素・属性を残す形が基本です。1ファイルずつ手軽にチェックしたい場合は SVG Sanitizer のようなブラウザ完結のツールを使うと、コードを貼り付けて危険要素の除去とプレビュー確認が同時に行えます。

read next