LoginSignup
33
28

More than 5 years have passed since last update.

getimagesize/finfoのMIMEタイプ判定はザルい

Last updated at Posted at 2014-07-04

http://qiita.com/sakana_kirai/items/6e512f7aea2898a6e8f2
http://qiita.com/mpyw/items/939964377766a54d4682

アップロードされたファイルについて、getimagesize()やfinfo::file()でMIMEタイプを判定しています。

<?php
    // hoge.pngを判定
    $imagesize = getimagesize('path/to/hoge.png');
    $finfo = new finfo(FILEINFO_MIME_TYPE);
    $finfofile = $finfo->file('path/to/hoge.png');
    var_dump($imagesize, $finfofile);

ここでhoge.pngの中身を以下のように書き換えてみます。

    GIF8<script>alert("xss");</script>

結果。

array(6) {
  [0]=>
  int(29283)
  [1]=>
  int(28777)
  [2]=>
  int(1)
  [3]=>
  string(28) "width="29283" height="28777""
  ["channels"]=>
  int(3)
  ["mime"]=>
  string(9) "image/gif"
}
string(9) "image/gif"

なんと頭に「GIF8」と書いておくだけでMIMEタイプの判定をすり抜けてしまいました。

まあMIMEタイプがimage/gifだとわかったので、ファイルは見えないところに隠しておいて、出力時にきちんとContent-Typeヘッダを出してあげれば問題ありません。

<?php
    header('Content-type: image/gif');
    readfile('path/to/hoge.png');

これで完璧!

finfo.png

お、おう。

これはIEでのみ発生する可能性のあるXSSです。
IEは設定されたContent-Typeをあまり気にせず、ファイルの中身を見て適切な形式で出力してくれるという素敵極まりない機能が付いています。
で、IEは「GIF8」をgifとはみなさず、後続のHTMLを見てHTMLと判断してくれやがるようです。
「GIF87」にすると画像と判断されます。

X-Content-Type-OptionsというHTTPヘッダによって、この余計で傍迷惑な機能を止めることができます。
http://d.hatena.ne.jp/hasegawayosuke/20110106/p1
http://blog.everqueue.com/chiba/2011/01/06/484/

<?php
    header('Content-type: image/gif');
    header('X-Content-Type-Options: nosniff');
    readfile('path/to/hoge.png');

これでIEでもXSSが発生しないようになります。
結論としては、とりあえずあらゆる出力にX-Content-Type-Optionsを書いとけ、というところでしょうか。

ちなみに
http://d.hatena.ne.jp/hasegawayosuke/20110106/p1

X-Content-Type-Options: nosniff つかわないやつは死ねばいいのに!

とか書いてるこのページにはX-Content-Type-Optionsが設定されていません。

33
28
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
33
28