と書かれている通り信頼できません。ソースコードを見てみると$_FILES['userfile']['type'] ファイルの MIME 型。ただし、ブラウザがこの情報を提供する場合。 例えば、"image/gif" のようになります。 この MIME 型は PHP 側ではチェックされません。そのため、 この値は信用できません。
php-5.1.4/main/rfc1867.c
の SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler)
という関数の中でブラウザから送られた Content-Type
をそのまま代入していることがわかります。WindowsのFirefoxの場合、ブラウザはファイルの拡張子だけを見て Content-Type を送ってくるので中身とtypeが一致しないこともあります。
それで Building Scalable Web Sites: Building, scaling, and optimizing the next generation of web application の "Wireless Carriers Hates You" という部分。最近はあんまりへんなヘッダを送ってくる端末はなくなりましたが、アメリカでも携帯電話の送ってくるメールは写真がBASE64でエンコードされているけど content-type が text/plain だったりするそうで、だから中身読んで調べるしかないよ、というのを思い出しました。
getimagesize
は引数にファイルしかとらないので、メモリ上にデータがあるときは一度ファイルに書き込む必要があります。そんなに実用性はありませんが、ここでもったいない精神を発揮して自分で画像のフォーマットを調べてみましょう。JPEGとGIFとPNGだけでよければアタマの8バイトを見ることで判別可能です。
GIF87a
もしくは GIF89a
になっています。
\x89PNG\x0d\x0a\x1a\x0a
なぜこの8バイトなのか PNG - Wikipedia, the free encyclopedia に書かれているのですが、これがよく考えられていています。
1バイト目の0x89は、データの転送路が8ビットクリーンかどうかを調べるためのものです。昔アメリカ人がネットワークでデータをやり取りするときにASCIIだけなら8ビット目は常に0だから節約できるよね、ということで8ビット目を節約するようにしたと聞きましたが詳しいことはよく知りません。あとからやっぱりASCIIだけじゃ困るということでバイナリをASCIIテキストにエンコードするBASE64だとか日本語を8ビット目を使わないようにエンコードする ISO-2022-JP(JIS) だとかで苦労しているのはその名残です。いまでも8ビット目がなくなっちゃうようなネットワークはあるのでしょうか。
2バイト目から4バイト目までの"PNG"はテキストエディタ等で人間が見たときにわかりやすいように入れられています。
3バイト目と4バイト目の 0x0D 0x0A は、Windowsの改行コードになっています。ファイルがテキストモードで転送されていると、画像データの中のこの並びがすべてそれぞれのプラットフォームに合わせて変わってしまい、データが壊れてしまいます。この2バイトが 0x0D 0x0A になっているかを調べることで、改行コードの変換が行われて転送時にデータが壊れていないかがわかります。
4バイト目の0x1Aは、MSDOSの元になったCP/Mのファイルシステムでファイルの終端を示していた値です。DOSプロンプトでTYPEコマンドを使ってファイル中身を表示すると、このコードがでできた場所をファイルの終端とみなしてそれ以降は表示されません。これはかなり実用性が低くてマニアックです。
5バイト目の 0x0A はUNIX系システムの改行コードです。3-4バイト目の 0x0D 0x0A 同様、改行コードが変換されていないかどうかを調べるためにあります。
if ( preg_match( '/^\x89PNG\x0d\x0a\x1a\x0a/', $image_stream) ) {
$type = "png";
} elseif ( preg_match( '/^GIF8[79]a/', $image_stream) ) {
$type = "gif";
} elseif ( preg_match( '/^\xff\xd8/', $image_stream) ) {
$type = "jpg";
}
というコードで画像のフォーマットを判別できます。
/usr/share/magicの再発明のような気が・・・というコメントをいただいたので、さっそくmagicファイルを調べました。 ファイルの内容を見て種類を調べる
file
コマンドが、いろいろなファイルのヘッダが記述されているmagicファイルは知りませんでした。
OSXのmagicファイルのJPEGファイルヘッダ定義は
# JPEG images
# SunOS 5.5.1 had
#
# 0 string \377\330\377\340 JPEG file
# 0 string \377\330\377\356 JPG file
#
# both of which turn into "JPEG image data" here.
#
0 beshort 0xffd8 JPEG image data
こうなっています。
アップロードされたファイルのフォーマットをいちおうテストしたい、というときにはmagicを見てどうチェックしたらいいかを調べるとよさそうです。
ちなみにOSによってmagicファイルがとこにあるかはまちまちなようで、それぞれ
/usr/share/file/magic
/etc/magic
/usr/share/magic
man file
で下の方を見ると載っています。
トラックバック元エントリにこのエントリへのリンクがない場合はトラックバックを受け付けません。
http://labs.gmo.jp/mt/mt-tb.cgi/126
comments