Posted at

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

More than 1 year has 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が返ってきたところ

縦がチェックしたエンコード、横が検出対象のエンコード。

予想以上にアテにならないようです。