WordPress で一部の .xlsファイル だけがアップロードできない (失敗)

WordPressサイト で一部の .xlsファイル だけがアップロードできない現象に遭遇したため、対処しました。

現象

とある WordPressサイト で .xlsファイル をアップロードしようとすると以下のエラーが表示されました。

“XXXXXXXXXXX.xls” のアップロードに失敗しました。

このファイルタイプはセキュリティ上の理由から、許可されていません。

「“XXXXXXXXXXX.xls” のアップロードに失敗しました。このファイルタイプはセキュリティ上の理由から、許可されていません。」のエラー
「“XXXXXXXXXXX.xls” のアップロードに失敗しました。このファイルタイプはセキュリティ上の理由から、許可されていません。」のエラー

しかも困ったことに、「Aの .xlsファイル はOKだが、Bの .xlsファイル はNG」というように、ファイルによって現象が分かれました。

両方とも正常に開ける .xlsファイル ですし、見た目上は同じなのですが……。

対処1

最初の対処として、 wp-config.php にファイルアップロードの際のフィルタリングをオフにする方法を試しました。

<?php

// 略

$table_prefix  = 'wp_';

// 略

define('WP_DEBUG', false);

define('ALLOW_UNFILTERED_UPLOADS', true); // この1行を追記

/* 編集が必要なのはここまでです ! WordPress でブログをお楽しみください。 */

// 略

これでファイルアップロードを試したところ、2つの .xlsファイル 共にアップロードできるようになりました。

ただし、この方法ですとファイルアップロードの際のフィルタ処理を全てオフにしてしまうため、セキュリティ的に好ましくありません。

そこで、もう少し調査を続けることにしました。

検証

上述の対処1より、ファイルアップロードの際のフィルタリング処理で引っかかっていることは分かりました。

このフィルタリングは、拡張子と MIMEタイプ の判定をしているということが分かりました。

ファイルとしては、 wp-includes/functions.php です。

中身を見て、関数 wp_get_mime_types() で拡張子と MIMEタイプ の一覧を持っていることが分かります。

この処理にアクションフックを引っかけることができれば良さそうです。

MIMEタイプ の確認

次に、問題のファイルの MIMEタイプ を確認します。

試しに、以下のような構成を作ります。

   /
  ├ files/
  │    ├ sample1.xls  // ファイルアップロード OK の .xlsファイル
  │    └ sample2.xls  // ファイルアップロード NG の .xlsファイル
  │
  └ index.php

また、 index.php

この記事を参考にして、以下のようにします。

<?php

$paths = [
    './files/sample1.xls',
    './files/sample2.xls',
];

// finfoクラスを使う
$finfo = new finfo();

foreach ($paths as $key => $value) {
    echo $value . ": ";
    echo $finfo->file( $value, FILEINFO_MIME_TYPE );
    echo "<br><br>\n\n";
}

これを問題が発生している WordPressサイト と同じサーバにアップロードします。

ファイルアップロード NG のファイルの MIMEタイプ が「application/vnd.ms-office」になっている
ファイルアップロード NG のファイルの MIMEタイプ が「application/vnd.ms-office」になっている

すると、ファイルアップロード OK の .xlsファイルは MIMEタイプが application/vnd.ms-excel だったのに対し、 NG だった .xlsファイル は application/vnd.ms-office でした。

他の環境でのテスト
他の環境でのテスト

ちなみに、他の環境に同じファイル群をアップロードしたところ両方とも application/vnd.ms-excel で、この環境では今回の現象は発生しませんでした。

MIMEタイプ は .xls の場合 application/vnd.ms-excel のはずですが……。

先に結論から言ってしまえば、利用しているサーバーのPHPのバージョンが低く、PHPの中で呼び出される「Fileinfo」というファイル操作のモジュールのバージョンが低かった為、アップロードしたエクセルファイルの正しいMIMEタイプが認識がされていないという状態になっていました。

WordPressのメディアにアップロードが出来ない時 | スタッフブログ | 株式会社クーネルワーク

PHP のバージョンによって Fileinfoモジュール の判定結果が異なるとのこと。 WordPress の MIMEタイプ 判定もこのモジュールを得利用しているとのことで、ここで影響を受けてしまうようです。

以上より、今回の環境では本来 application/vnd.ms-excel となるべき MIMEタイプ が、何故か application/vnd.ms-office と判定され、結果、 WordPress のファイルアップロードの際のフィルタリング処理の中の MIMEタイプ 判定で引っかかっていた、というのが今回の現象の原因でした。

対処2

以上の検証を踏まえて対処をします。具体的には、アクションフックでファイルアップロードの際のフィルタリング処理の際に application/vnd.ms-office も許可するようなプラグインを作成します。

<?php
/*
Plugin Name: MIMEUruwashii
Description: PHP の MIME タイプ判定で特定の .xlsファイル が弾かれる現象への対策
Version: 0.0.1
Author: アルム=バンド
*/

/**
 * MIMEUruwashii : PHP の MIME タイプ判定で特定の .xlsファイル が弾かれる現象への対策
 * 
 */
class MIMEUruwashii
{
    /**
     * __construct : コンストラクタ
     *
     */
     public function __construct() {
        add_filter(
            'upload_mimes',
            [
                $this,
                'Bibishii',
            ]
        );
    }
    /**
     * Bibishii : フィルター追加
     *
     * @param {Object} : MIMEタイプ の一覧のオブジェクト
     * 
     * @return {Object} : 誤判定している MIME タイプを追記する
     *
     */
    public function Bibishii ( $mimes )
    {
        $mimes['xls'] = 'application/vnd.ms-office';
        return $mimes;
    }
}

// instantiate
$ab_wp_plugin_mimeuruwashii = new MIMEUruwashii();

このプラグインを .zip でアップロード、インストールして有効化します。

もちろん、 wp-config.phpALLOW_UNFILTERED_UPLOADS の行は削除して試験しました。

……が、挙動は変わらず、解決できませんでした。

参考

ALLOW_UNFILTERED_UPLOADS

WordPress 内のファイルアップロードのフィルタ処理について

Fileinfoモジュール による MIMEタイプ 判定

MIMEタイプ の一覧

この記事を書いた人

アルム=バンド

フロントエンド・バックエンド・サーバエンジニア。LAMPやNodeからWP、Gulpを使ってejs,Scss,JSのコーディングまで一通り。たまにRasPiで遊んだり、趣味で開発したり。