WordPress で SVG をアップロードできるようにする functions.php の書き方|安全に許可する条件

結論

WordPress は標準では SVG ファイルのアップロードを許可していません。メディアライブラリに SVG を入れたいときは、functions.php(またはプラグイン)で upload_mimes フィルターに 'svg' => 'image/svg+xml' を足すと、アップロードできるようになります。

ただし、SVG は画像であると同時に、中にスクリプトを書ける XML 文書です。誰でもアップロードできる状態にすると、悪意のある SVG を通じたクロスサイトスクリプティング(XSS)の入口になり得ます。そのため、許可するときは次の条件を守ることを勧めます。

  1. 信頼できるユーザー(管理者など)だけに許可する
  2. 外部から受け取った SVG は、中身を確認・無害化してからアップロードする
  3. ロゴやアイコンなど、テーマで固定して使う SVG は、メディアライブラリを通さずテーマのファイルとして置くことも検討する

原因・仕組み

アップロードできるファイルの種類は、upload_mimes フィルター(WordPress 2.0.0 から)を通した「拡張子 → MIME タイプ」の一覧で決まります。フィルターには一覧の配列 $t と、対象ユーザー $user が渡されます。SVG はこの一覧に含まれていないので、拡張子だけ見ても、ファイルの中身を見ても「許可されていない種類」と判定されます。

アップロード時の確認は、wp_check_filetype_and_ext() が行います。この関数はファイルの実際の種類を調べ、拡張子と合わないときは正しい値を返そうとします。許可一覧に入っていない種類は、ext と type が false になり、アップロードが拒否されます。

一覧を無視して何でもアップロードできる権限 unfiltered_upload もありますが、公式の Roles and Capabilities によると、この権限は既定ではどのロールにも与えられておらず(Super Admin を含む)、ALLOW_UNFILTERED_UPLOADS 定数を定義したときだけ付与できます。SVG のためにこれを有効にするのは、許可範囲が広すぎます。

SVG の危険性は仕様から来ています。W3C の SVG 2 仕様は、SVG の script 要素を HTML の script 要素と同等のものと定め、onclick などの on で始まるイベント属性も定義しています。つまり、見た目は画像でも、ブラウザで直接開くとスクリプトが動く作りにできます。WordPress の Security ハンドブックが説く「どんなデータも信用しない」という原則は、アップロードされたファイルにも当てはまります。

手順(サンプルコード)

テーマの functions.php、または自作プラグインに書きます。管理者(manage_options を持つユーザー)だけに SVG を許可する例です。

add_filter( 'upload_mimes', 'mytheme_allow_svg_for_admins' );
function mytheme_allow_svg_for_admins( $mimes ) {
    if ( current_user_can( 'manage_options' ) ) {
        $mimes['svg'] = 'image/svg+xml';
    }
    return $mimes;
}

次は悪い例です。そのまま使わないでください。

// 悪い例:投稿者や寄稿者を含む全員に SVG を許可してしまう
add_filter( 'upload_mimes', function ( $mimes ) {
    $mimes['svg'] = 'image/svg+xml';
    return $mimes;
} );

テーマで固定して使うロゴなどは、テーマ内のファイルを読み込めばメディアライブラリの許可は要りません。

<img src="<?php echo esc_url( get_theme_file_uri( 'assets/logo.svg' ) ); ?>" alt="<?php echo esc_attr( get_bloginfo( 'name' ) ); ?>" width="160" height="40">

特に注意したいのは、アップロードされた SVG の URL を直接開いたとき(SVG 文書としてブラウザに解釈される)や、SVG の中身をそのまま HTML に埋め込んだときです(ブラウザごとの細かな扱いはこの記事では検証していません)。外部から受け取った SVG の中身を echo で直接出力するのは避けてください。

動作確認(検証環境と結果)

WordPress 7.1.2(ja)/PHP 8.2.12 の CLI 検証環境で、四角形だけの SVG ファイルを作り、許可前・管理者への許可後・編集者で判定を比べました(php run.php wl2_svg.php、ログは tests/svg-upload-functions-php-security.md)。

finfo detects=image/svg+xml
admin unfiltered_upload=false / svg in get_allowed_mime_types=false
default: wp_check_filetype type=false / and_ext ext=false type=false
with upload_mimes (admin): wp_check_filetype type='image/svg+xml' / and_ext ext='svg' type='image/svg+xml'
with upload_mimes (editor): wp_check_filetype type=false / and_ext ext=false type=false
  • 標準では SVG は許可一覧に無く、wp_check_filetype_and_ext() も false を返しました(=アップロード拒否)。
  • 管理者でも unfiltered_upload は false でした(公式の説明どおり、既定では無い権限)。
  • 上の関数を入れると、管理者では svg / image/svg+xml と判定され、編集者では引き続き false でした。管理者だけに絞れていることが確認できます。
  • テスト用のファイルとユーザーは削除しました。ブラウザでの実際のアップロード画面、サーバーの PHP 拡張(fileinfo)が無い環境での挙動は確認していません。

WordPress で SVG をアップロードできるようにする functions.php の書き方の注意点

  • 許可しても、アップロードされた SVG の中身は無害化されません。WordPress 本体には SVG のスクリプトを取り除く機能は無いので、外部の SVG は中身を確認し、script 要素や on で始まる属性が無いものだけを使います。
  • 管理者以外にも許可したい場合は、SVG を無害化する仕組み(実績のあるプラグインやライブラリ)を併用することを勧めます。
  • マルチサイトでは、ネットワーク管理の「アップロードを許可するファイルの種類」の設定も関係します。
  • 許可の判定は upload_mimes に依存するので、ほかのプラグインが同じフィルターで SVG を消している場合は優先度(第3引数)を確認します。

WordPress で SVG をアップロードできるようにする functions.php の書き方でよくあるミス

  • 「このファイルタイプをアップロードする権限がありません。」(検証環境 7.1.2 の日本語訳)を消すために、ALLOW_UNFILTERED_UPLOADS を true にしてしまう。
  • 全員に SVG を許可し、寄稿者・投稿者の権限で危険なファイルが入れられる状態にする。
  • フィルターで $mimes を return し忘れ、全種類のアップロードができなくなる(add_filter で return を忘れると|空表示・NULL の原因と対処(準備中))。
  • ネットで拾った SVG アイコンを、中身を見ずに file_get_contents() してそのまま出力する。

WordPress で SVG をアップロードできるようにする functions.php の書き方のチェックリスト

  • [ ] SVG を許可するのは管理者など信頼できるユーザーだけにした
  • [ ] ALLOW_UNFILTERED_UPLOADS を使っていない
  • [ ] フィルターで $mimes を必ず返している
  • [ ] 外部の SVG は中身(script 要素・on 属性)を確認した
  • [ ] テーマ固定の SVG はテーマ内に置き、esc_url() で出力した
  • [ ] SVG の中身を HTML にそのまま埋め込んでいない

WordPress で SVG をアップロードできるようにする functions.php の書き方のFAQ(よくある質問)

Q. 許可したのに「許可されていません」と出ます。
A. ログイン中のユーザーが条件(manage_options)を満たしているか、別のプラグインが同じフィルターで上書きしていないかを確認します。

Q. アイキャッチに SVG を使えますか。
A. アップロードできれば設定はできますが、テーマによっては画像サイズの情報が無いため表示が崩れることがあります(未検証)。表示側で width・height を明示して整えます。

Q. SVG の代わりに何を使うのが安全ですか。
A. 写真やイラストは PNG・WebP などのラスター画像、テーマのアイコンはテーマ内の SVG ファイルにすると、アップロードの許可を広げずに済みます。

筆者の見解(WordPress で SVG をアップロードできるようにする functions.php の書き方)

SVG の許可は、数行のコードで済むぶん、誰に許可しているのかを考えないまま入れられがちだと感じます。私見では、「管理者だけに許可する」「外部の SVG は中身を見る」の2点を守れば、ロゴやアイコンの運用には十分で、それ以上に広げるなら無害化の仕組みとセットにするべきです。検証でも、ユーザーの権限で許可を絞れることが確認できたので、まずは最小の範囲から始めることを勧めます。

WordPress で SVG をアップロードできるようにする functions.php の書き方の関連項目

出典(一次情報)

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