Help us understand the problem. What is going on with this article?

PHPの文字エンコーディング検出はアテにならない

More than 3 years have passed since last update.

一身上の都合で文字エンコーディングと戦った時のメモ

PHPにおける文字列のエンコーディングはphp-mbstringモジュールで提供されている。
mb_detect_encodingはデフォルトではASCIIとUTF-8しかチェックしてくれない。
mb_detect_orderでチェックするエンコーディング形式を指定することができる。
だがしかし、対して検出精度はよくない。
不適切なエンコーディングで変換された場合、脆弱性になり得る。

テスト環境

  • OS : CentOS7
  • PHP : 7.0.11

エンコードテスト

UTF-8の文字列をmb_convert_encodingで各文字エンコーディングで変換し、mb_detect_encodingmb_check_encodingで検出させる。

    $str = 'てすと';

    $enc = [
      'UTF-8',
      'UTF-7',
      'ASCII',
      'EUC-JP',
      'SJIS',
      'eucJP-win',
      'SJIS-win',
      'JIS',
      'ISO-2022-JP',
      'Unicode',
    ];
    mb_detect_order($enc);

    foreach ($enc as $e) {
      $encoded = mb_convert_encoding($str, $e, 'UTF-8');
      var_dump(
            $e,
            $encoded,
            mb_detect_encoding($encoded),
            mb_check_encoding($encoded)
      );
    }

結果

エンコード var_dump mb_detect_encoding mb_check_encoding
UTF-8 てすと UTF-8 true
UTF-7 +MGYwWTBo- UTF-8 true
ASCII ??? UTF-8 true
EUC-JP "" EUC-JP true
eucJP-win "" EUC-JP true
SJIS "" SJIS true
SJIS-win "" SJIS true
JIS \$B\$F\$9\$H(B UTF-8 true
ISO-2022-JP \$B\$F\$9\$H(B UTF-8 true
Unicode 0f0Y0h UTF-8 true

SJISとかは一部表示されていない(バイナリとして解釈されている?)けどエンコーディングは取得できていた。
mb_check_encodingはすべての場合で検出できていた。
しかし適切に検出できているのか、とにかくtrueを返す残念な子なのか調べてみる。

エンコード検出テスト

    $str = 'てすと';

    $enc = [
      'UTF-8',
      'UTF-7',
      'ASCII',
      'EUC-JP',
      'eucJP-win',
      'SJIS',
      'SJIS-win',
      'JIS',
      'ISO-2022-JP',
      'Unicode',
    ];
    mb_detect_order($enc);

    foreach ($enc as $e) {
      $s = mb_convert_encoding($str, $e, 'UTF-8');
      $arr = [];
      foreach ($enc as $e2) {
        $arr[$e2] = mb_check_encoding($s, $e2);
      }
      var_dump($e, $arr);
    }

結果

チェック\エンコ UTF-8 UTF-7 ASCII EUC-JP eucJP-win SJIS SJIS-win JIS ISO-2022-JP Unicode
UTF-8 o o o o o o
UTF-7 o o o
ASCII o o o o o
EUC-JP o o o o o o o
eucJP-win o o o o o o o
SJIS o o o o o o o o o o
SJIS-win o o o o o o o o o o
JIS o o o o o
ISO-2022-JP o o o o o
Unicode o o o o o o o o

o…trueが返ってきたところ
縦がチェックしたエンコード、横が検出対象のエンコード。
予想以上にアテにならないようです。

ichi_404
`${ new Date().getFullYear() - 2015 }` 年目 PHP、JavaScript、猫と戯れる。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away