《PHP8上級試験対策》ファイルアップロード時に $_FILES[‘type’] を信用してはいけない理由

  • URLをコピーしました!

はじめに

アップロードフォームを使えば、ユーザは自由にファイルをサーバに送信できます。しかし、送られてくるファイル名・拡張子・タイプ情報は、すべてユーザ側の情報。つまり、悪意を持って簡単に「偽装」できてしまうのです。

「画像ファイルに見せかけた実行ファイル」などを防ぐには、サーバ側で内容を検証し、安全に保存する仕組みが不可欠です。

キーワード:ファイルアップロード / $_FILES / MIMEタイプ / finfo_file / is_uploaded_file / move_uploaded_file / セキュリティ対策 /

目次

■ ファイルアップロードの流れと落とし穴

PHPでは、ファイルアップロードフォームを作ると以下のように $_FILES 配列に情報が入ります。

$_FILES['userfile'] = [
  'name'     => 'cat.png',
  'type'     => 'image/png',
  'tmp_name' => '/tmp/phpXXXX.tmp',
  'error'    => 0,
  'size'     => 12345
];

🧩 ASCIIアート図解:「ブラウザ → PHPサーバ」構造イメージ

┌──────────────────────┐
│   ユーザのブラウザ      │
│  ──────────────      │
│  <input type="file"> │
│  name="userfile"     │
└──────────────────────┘
           │ アップロード
           ▼
┌───────────────────────┐
│   PHPサーバ側($_FILES) │
│  ┌────────────────┐  │
│  │ name: "cat.png"   │ ← ❌ 元のファイル名(信用できない)
│  │ type: "image/png"  │ ← ❌ クライアント指定(偽装可能)
│  │ tmp_name: "/tmp/..."│ ← ✅ 実際に保存された場所
│  └──────────────────┘ │
└───────────────────────┘

ここで注意すべきは、nametypeブラウザ依存の情報だということ。悪意あるユーザは簡単に偽装して送ることができます。

■ よくある誤解:「$_FILES[‘type’] でチェックすればOK」?

実はこれ、誤りです。$_FILES['type']ブラウザが送る HTTP ヘッダの値であり、送信ツールを使えば簡単に任意の値をセットできます。

例えば──

本当の中身:PHPスクリプト
偽装ヘッダ:Content-Type: image/jpeg

これでも $_FILES['type']"image/jpeg" と見えてしまいます。
つまり、サーバ側で再チェックしないと危険です。

■ 安全なチェック方法(実務でも使える)

  1. $_FILES['userfile']['error']UPLOAD_ERR_OK か確認
  2. is_uploaded_file() で正規アップロードかチェック
  3. finfo_file() で MIME タイプを再判定
  4. 許可MIME(ホワイトリスト)と照合
  5. 拡張子はサーバ側で付与(元の name を使わない)
  6. move_uploaded_file() で保存
  7. 保存先は webroot の外がベスト

💡 コード例(安全版)

$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime  = $finfo->file($_FILES['userfile']['tmp_name']);

$allowed = [
  'image/jpeg' => 'jpg',
  'image/png'  => 'png',
  'image/webp' => 'webp',
];

if (!isset($allowed[$mime])) {
  throw new RuntimeException('不正なファイルタイプです: ' . $mime);
}

$filename = bin2hex(random_bytes(16)) . '.' . $allowed[$mime];
move_uploaded_file($_FILES['userfile']['tmp_name'], __DIR__ . '/uploads/' . $filename);

■ 拡張子やファイル名をそのまま使うリスク

  • 重複ファイル名:同名ファイルが上書きされる危険
  • パスインジェクション../../../ のような経路で意図しない場所に書き込み
  • XSS対策:ユーザ入力を HTML に出すときは必ず htmlspecialchars()

🧠 試験対策まとめ(ここが出る!)

チェック対象信用できる?備考
$_FILES['name']元のファイル名。重複・改竄の恐れあり
$_FILES['type']ブラウザ送信値。偽装可能
$_FILES['tmp_name']実際の一時保存ファイルパス
finfo_file()サーバ側で判定する正しい方法
move_uploaded_file()正しいアップロードのみ許可

■ 図解まとめ:「正しい判定フロー」

          ▼
   ユーザがアップロード
          │
          ▼
  PHPが$_FILESを受け取る
          │
          ▼
 [誤] $_FILES['type']で判断 → ❌偽装される
          │
          ▼
 [正] finfo_file() でMIME判定 → ✅サーバ側で安全確認
          │
          ▼
 move_uploaded_file()で保存

🏁 まとめ

PHPでファイルアップロードを扱う際は、「ファイル名・拡張子・タイプ情報を絶対に信用しない」のが鉄則。

$_FILES['type'] はヒント程度に考え、サーバ側で finfo や getimagesize で再検証するのが安全です。

この記事が気に入ったら
いいねしてね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次