2026年10月16日
2026年10月16日
WordPressでPHPファイルのアップロードと不正実行を防ぐ設定
WordPressのメディアアップロードは、標準ではPHPファイルを受け付けません。それでも改ざん被害で「アップロードフォルダにPHPが置かれて実行された」という例が後を絶たないのは、脆弱なプラグインの独自アップロード処理や、サーバー側の設定の甘さを経由するからです。対策は「アップロードできる種類を絞る」「置かれても実行させない」「管理画面からのファイル書き換えを止める」の3層で考えます。この記事では、それぞれの設定方法と、設定が効いているかの確かめ方を説明します。
WordPress標準のアップロード制限を理解する
メディアライブラリやエディターからのアップロードは、WordPressコアの wp_handle_upload() を通ります。この中で wp_check_filetype_and_ext() が、ファイル名の拡張子が許可リスト(get_allowed_mime_types() が返す一覧)にあるか、画像などでは中身の形式と拡張子が食い違っていないかを確認します。.php はこの許可リストに入っていないため、通常の操作ではアップロードできません。
また、ファイル名は sanitize_file_name() で整えられ、shell.php.jpg のように途中に許可されていない拡張子が含まれる名前は、shell.php_.jpg のように区切りが変えられます。これは、サーバー設定によっては「.php を含むファイル名」をPHPとして実行してしまう問題への備えです。
ただし、この仕組みには前提があります。
unfiltered_upload権限を持つユーザーは種類のチェックを受けません。この権限は通常どのユーザーにも付いていませんが、wp-config.php にALLOW_UNFILTERED_UPLOADSを true で定義すると、シングルサイトの管理者に付与されます。一時的に使ったまま消し忘れている例があるので確認してください。- 管理者のように
unfiltered_html権限を持つユーザーは、HTMLやJavaScriptファイルもアップロードできる設定になっています。 - プラグインが
move_uploaded_file()などで独自にファイルを保存している場合、コアのチェックは通りません。過去の重大な脆弱性の多くはこの部分にありました。
つまり、コアの制限は「正規の画面からの操作」を守るもので、プラグインの欠陥やサーバーへの直接の書き込みまでは防げません。そこで次の層が必要になります。
対策1:アップロードできる種類をさらに絞る
使わないファイル形式は、upload_mimes フィルターで許可リストから外せます。テーマの functions.php ではなく、テーマ変更の影響を受けない mu-plugin(wp-content/mu-plugins/ に置くPHPファイル)に書くのがおすすめです。
<?php
/**
* Plugin Name: Upload type restriction
* Description: 使わないファイル形式をアップロード許可リストから外す
*/
add_filter( 'upload_mimes', function ( $mimes ) {
// サイトで使わない形式を外す(キーは「拡張子|拡張子」の形)
// HTML と JS は unfiltered_html 権限を持つユーザーにだけ許可されている形式
unset( $mimes['htm|html'], $mimes['js'] );
return $mimes;
} );
外す形式は、サイトの運用で本当に使っていないものに限ります。PDFを配布しているサイトで pdf を外すと、編集担当者が困ります。
あわせて wp-config.php で次の2点を確認します。
// 消し忘れがあれば削除する(定義しないのが既定)
// define( 'ALLOW_UNFILTERED_UPLOADS', true );
// 管理者を含め、全ユーザーの unfiltered_html 権限を外す(必要に応じて)
define( 'DISALLOW_UNFILTERED_HTML', true );
DISALLOW_UNFILTERED_HTML を有効にすると、投稿に任意のスクリプトを書くこともできなくなります。広告タグや計測タグを本文に直接貼る運用をしている場合は影響が出るので、事前に確認してください。
対策2:アップロードフォルダでPHPを実行させない
アップロード経路をいくら絞っても、脆弱なプラグインなどを経由して wp-content/uploads/ にPHPが置かれる可能性は残ります。画像や文書を置く場所でPHPが実行される必要はないので、サーバー側で実行を止めます。これが最も効果の大きい層です。
Apache の場合
wp-content/uploads/ に .htaccess を作成します(既にある場合は、中身をバックアップしてから追記します)。
# uploads 配下のPHP系ファイルへのアクセスを拒否する
<FilesMatch "\.(?i:php[0-9]?|phtml|phar|phps)$">
Require all denied
</FilesMatch>
php7 や phtml のような別の拡張子でもPHPとして扱うサーバーがあるため、まとめて拒否しています。
Nginx の場合
server ブロックの、PHPをFastCGIに渡す location ~ \.php$ よりも前に書きます。Nginxの正規表現 location は上から順に評価され、最初に一致したものが使われるためです。
location ~* ^/wp-content/uploads/.*\.(php[0-9]?|phtml|phar|phps)$ {
deny all;
}
sudo nginx -t で文法を確認してから reload します。
対策3:管理画面からのファイル編集を止める
管理者アカウントが乗っ取られると、テーマ・プラグインのファイルエディターからPHPを直接書き換えられてしまいます。wp-config.php に次を追加すると、このエディターが無効になります。
define( 'DISALLOW_FILE_EDIT', true );
さらに強い DISALLOW_FILE_MODS を true にすると、管理画面からのプラグイン・テーマの追加と更新もできなくなります。更新をWP-CLIやデプロイの仕組みで行っている環境向けで、管理画面から更新している運用では、更新漏れのほうが危険になるため慎重に判断してください。
ファイルの権限も見直します。PHPの実行ユーザーが書き込める必要があるのは、基本的に wp-content/uploads/ とキャッシュ系のフォルダです。コアやテーマのファイルまで書き込み可能になっていると、侵入時に改ざんされる範囲が広がります。ただし、権限を変えると管理画面からの更新が動かなくなる構成もあるので、サーバーの推奨設定に合わせてください。
設定が効いているか確かめる
設定後は、実際に試すのが一番確かです。ステージング環境で行うのが理想ですが、本番で行う場合も無害な内容のファイルを使い、確認後すぐに削除します。
- SFTPで
wp-content/uploads/にtest-exec.php(中身は<?php echo 'executed';)を置く - ブラウザや curl で
https://example.com/wp-content/uploads/test-exec.phpを開く - 403 になれば成功。
executedと表示されたら実行を止められていない - テスト用ファイルを削除する
curl -s -o /dev/null -w "%{http_code}\n" https://example.com/wp-content/uploads/test-exec.php
あわせて、既にPHPが置かれていないかを点検します。
# uploads 配下のPHP系ファイルを一覧にする
find wp-content/uploads -type f \( -iname "*.php*" -o -iname "*.phtml" -o -iname "*.phar" \)
プラグインによっては、フォルダの中身を一覧表示させないために空の index.php を置いていることがあります。中身が「Silence is golden」のようなコメントだけのものは正常です。見覚えのないPHP、難読化された長いコード、eval や base64_decode を多用したファイルが見つかった場合は、単に削除するだけで済ませず、侵入経路の調査、パスワードの変更、プラグインの更新までを一連の対応として行ってください。
つまずきやすいポイント
- レンタルサーバーで .htaccess が効かない:Nginxで動いているサーバーでは .htaccess は読まれません。サーバー会社が独自の設定画面を用意していることがあるので、マニュアルを確認してください。各社の機能名や設定場所は変わることがあります(2026年10月現在)。
- 一部の機能が壊れた:プラグインの中には、uploads 配下にPHPを生成して動かす古い設計のものがまれにあります。動作確認で不具合が出たら、そのプラグインの代替を検討するのが安全です。
- フォームの添付ファイル:お問い合わせフォームなどの添付ファイルは、プラグインが独自のフォルダに保存することがあります。そのフォルダも実行禁止の対象に含めるか、プラグインの保護機能を確認してください。
まとめ
- WordPressコアは標準でPHPのアップロードを拒否するが、プラグインの独自処理や権限の設定次第ですり抜けがある
upload_mimesで使わない形式を外し、ALLOW_UNFILTERED_UPLOADSの消し忘れを点検する- uploads 配下ではサーバー側でPHPの実行を拒否する(Apacheは .htaccess、Nginxは location の順序に注意)
DISALLOW_FILE_EDITで管理画面からのファイル書き換えを止める- テストファイルで403になることを確認し、既存のPHPファイルが置かれていないかも点検する