画像アップロード時の脆弱性について - PHPプロ!Q&A掲示板

4347

  • 0P

画像アップロード時の脆弱性について

質問日時 / 2014年7月1日 20:37    回答数 / 2件

Questioner:  hiro112  このエントリーをはてなブックマークに追加 

キーワード / 画像アップロード    脆弱性   

こんにちは、画像アップロードのフォームの脆弱性についてなのですが分からないことがたくさんありまして投稿いたしました。。

http://php.webtutor.pl/en/2011/05/13/php-code-injection-a-simple-virus-written-in-php-and-carried-in-a-jpeg-image/

このサイトでjpegのファイル自体にスクリプトを挿入する方法(でしょうか。。)を紹介されていますがこの方法の場合この攻撃を防ぐには、mimeタイプの判断に加えてなにかセキュリティ検査のソースを追加しなければなりませんでしょうか・・。> <;




http://forum.hashkiller.co.uk/forum-view.aspx?f=22

ログインフォームについてなのですが(画像アップロードと関係なくてすいません・・。)パスワードのハッシュはsha1を使用しています・・・。上のサイトでハッシュ化されたパスワードのデコードを依頼している人たちがいたのですがクラッカーの人たちに最近まだ解読されていないハッシュ関数はありますでしょうか..> <;




以下はアドバイスを頂いたり、サイトを参考にしながらできた画像アップロードのソースの一部なのですがこれですと簡単にデータベースに対して攻撃ができてしまうでしょうか。。


  1. /*====ファイルアップロード前の検査=====*/
  2. if(!empty($_FILES)){
  3.   
  4. if(!isset($_FILES['image']['error']) || !is_int($_FILES['image']['error'])
  5. ){  
  6. //エラー項目の確認
  7.  
  8. /*拡張子名からjpgであるか判断*/  
  9. /*====================================*/  
  10.   $fileName = $_FILES['image']['name'];
  11.   if (!empty($fileName)) {
  12.     $ext = substr($fileName, -3);
  13.     if ($ext != 'jpg') {
  14.       $error['image'] = 'type';
  15.     }
  16. /*======================================*/
  17.  
  18.  
  19. /*mimeタイプからjpgであるか判断*/
  20. /*======================================*/
  21. $finfo = new finfo(FILEINFO_MIME_TYPE);
  22. $type = $finfo->file($_FILES['image']['tmp_name']);
  23.  
  24. /*echo'$typeの内容は';
  25. echo $type;
  26. die;*/
  27.  
  28. if ($type != 'image/jpeg') {
  29.       $error['image'] = 'type';
  30.     }
  31. /*========================================================*/     
  32.  
  33. /*if(!isset($_FILES['upfile']['error']) || !is_int($_FILES['upfile']['error'])
  34. )の終了*/
  35.   }
  36. /*if(!empty($_FILES))の終了*/
  37. }
  38. /*//====ファイルアップロード前の検査=====*/
  39.  
  40.       
  41. if (empty($error)){
  42.       //画像をアップロードする
  43.     $_SESSION['coverimage'] = file_get_contents($_FILES['image']['tmp_name']);
  44.     
  45.     $_SESSION['edit'] = $_POST;
  46.     /*$_SESSION['edit']['image'] = $image;*/
  47.     header('Location: check.php'); exit();  
  48.   }
  49. }

この質問への意見の募集は締め切られ、ポイントは既に配分されました。
意見を投稿することはできますが、ポイントを受け取ることはできません。



ツリー一覧

┗A01To_aru_Userその記事で紹介されているのは、ファイルがPHPとして
 ┗A01-1hiro112いつもお世話になっております。(;-ω-`A) 添削

回答一覧

並び替え:

A01 満足
answererTo_aru_User [7月2日 03:36] (最終編集:7月2日 03:42)

その記事で紹介されているのは、ファイルがPHPとして実行されてしまったときに発生するセキュリティホールですね。対策としては…ファイルをPHPとして実行させなければいいだけの話です。極論を述べると、以下のケースのみが危険と見なされます。

「ユーザーがアップロードした【拡張子を含むかもしれないファイル名】を何のチェックもせずに【public_html内のディレクトリ】に保存する」

つまり…

・拡張子を全て排除するだけでも安全( PHPとして実行可能な拡張子のみでもOK: http://www.adminweb.jp/apache/php/index2.html 
・public_html内のディレクトリに置かなくするだけでも安全
・データベースに保存するだけでも安全

実はMIMEタイプのチェックはあんまり関係ありません。もちろん、ユーザーが誤ったファイルをアップロードしないようにやっておいたほうがいいですが。あまり過剰に怯える必要も無さそうですw

---------------------------------------------

データベースに画像データを保存するのでしたら

1. exif_imagetype関数やgetimagesize関数で「IMAGETYPE_XXX」定数の値を取得する。取得に失敗したときは画像ファイルではないので異常終了させる。
2. データベースにその定数値とともに画像データを保存する。

これだけで十分です。

---------------------------------------------

以下、私のまとめです(既に引用されているかもしれませんがw)

Qiita - ファイルアップロードの例外処理はこれぐらいしないと気が済まない
http://qiita.com/mpyw/items/939964377766a54d4682

Qiita - 画像アップロード処理サンプル集
http://qiita.com/mpyw/items/73ee77a9535cc65eff1e

Qiita - $_GET, $_POSTなどを受け取る際の処理
http://qiita.com/mpyw/items/2f9955db1c02eeef43ea

Qiita - リクエストパラメータ・セッションに関するまとめ
http://qiita.com/mpyw/items/7852213f478e8c5a2802

提示されたコードを私なりに最適化するのであれば以下のようになります。JPEGに限定してしまうのもユーザビリティが下がるので、メジャーな「GIF」「JPEG」「PNG」を許可するのが最もオススメです。

  1. <?php
  2.  
  3. /* 実はこの行の時点でもう「ファイルのアップロード」は終了しています。
  4.    「生成されたテンポラリファイル」をどう扱うかを以下に記述していくだけです。 */
  5.  
  6. // メッセージを表示する場合テキストとして処理させる
  7. // その際のレスポンスコードは「400 Bad Request」にする
  8. header('Content-Type: text/plain; charset=utf-8'true400);
  9.  
  10. // ファイル以外のパラメータがあればまずそれらをチェックする
  11. // $_POSTとしてまとめて代入するのではなくこの段階でチェックしておいた方が後がラク
  12. foreach (array('foo''bar''baz') as $key) {
  13.     // 未定義や配列としての不正送信もfilter_input関数を使うことで安全に処理する
  14.     $post[$key] = (string)filter_input(INPUT_POST$key);
  15.     if ($post[$key] === '') {
  16.         // 必要に応じて空文字列で異常終了させる
  17.         die("{$key}パラメータが未入力です");
  18.     }
  19. }
  20.  
  21. // 未定義である・複数ファイルである・$_FILES Corruption 攻撃を受けた
  22. // どれかに該当していれば不正なパラメータとして終了する
  23. if (!isset($_FILES['image']['error']) || !is_int($_FILES['image']['error'])) {
  24.     die('imageパラメータが不正です');
  25. }
  26.  
  27. // $_FILES['image']['error'] の値を確認
  28. switch ($_FILES['image']['error']) {
  29.     case UPLOAD_ERR_OK// OK
  30.         break;
  31.     case UPLOAD_ERR_NO_FILE:   // ファイル未選択
  32.         die('ファイルが選択されていません');
  33.     case UPLOAD_ERR_INI_SIZE:  // php.ini定義の最大サイズ超過
  34.     case UPLOAD_ERR_FORM_SIZE// フォーム定義の最大サイズ超過
  35.         die('ファイルサイズが大きすぎます');
  36.     default:
  37.         die('その他のエラーが発生しました');
  38. }
  39.  
  40. // ここで定義するサイズ上限のオーバーチェック
  41. // (必要がある場合のみ)
  42. if ($_FILES['image']['size'] > 1000000) {
  43.     die('ファイルサイズが大きすぎます');
  44. }
  45.  
  46. // $_FILES['image']['mime']の値はブラウザ側で偽装可能なので
  47. // MIMEタイプを自前で取得する
  48. if (!$post['coverimage_type'] = array_search(
  49.     @exif_imagetype($_FILES['image']['tmp_name']),
  50.     array(
  51.         IMAGETYPE_GIF,
  52.         IMAGETYPE_JPEG,
  53.         IMAGETYPE_PNG,
  54.     ),
  55.     true
  56. )) {
  57.     die('ファイル形式が不正です');
  58. }
  59.  
  60. // セッションを開始する
  61. // 実はこの関数、外部からのリクエスト次第でWarningを吐いてしまうので@をつけることを推奨
  62. @session_start();
  63.  
  64. // セッションに全てのデータを格納する
  65. $_SESSION = $post + array('coverimage_data' => file_get_contents($_FILES['image']['tmp_name']));
  66.  
  67. // 遷移する
  68. // 相対パス指定はHTTP1.1においては禁止されているのでHTTPURLの方が推奨される
  69. // ステータスコードはここで400から302に自動的に変更される(Locationヘッダは特殊で、明示的に302を指定しなくてもいい)
  70. header('Location: http://example.com/check.php');

この意見に回答する

ツリーへ TOPへ

A01-1
replyerhiro112 [7月2日 11:38]

いつもお世話になっております。(;-ω-`A) 

添削していただいたスクリプト大いに参考になりました!(ほぼそのまま使わせていただきました,,> <; )

はい、ブログの方拝見いたしまして、大いに参考になっております!


セキュリティー面はわからないことだらけなのでいつもためになることばかりです。> <;

本当に有難うございました > <;

この意見に回答する

ツリーへ TOPへ

<<質問一覧へ



Pick Up Q&A

Q
ログファイルの中の空のデータ行を削除したい
 このエントリーをはてなブックマークに追加 
A
ログのデータ個数(列数)が固定で、空のログが"<><><>"だと既知であれば if ($line === "<><><>") { continue; } で読み飛ばしてもいいのでは? ...

>>続きを読む

まずは配列や文字列の扱いから、じっくり勉強して行きましょう。

▲解説者:岡本(アシアル株式会社 教育コーディネーター兼 システムエンジニア)