Shikata Ga Nai

Private? There is no such things.

ファイルタイプ検証の欠陥:multipart/form-dataを悪用したファイルアップロード攻撃

Hello there, ('ω')ノ

✅ 通常のフォーム送信とファイル送信の違い

1. 📝 通常のフォーム送信

  • Content-Type: application/x-www-form-urlencoded
  • 主に テキストデータ(例:名前、メールアドレスなど)を送信

📋 送信例

name=John&email=john@example.com

2. 🖼️ ファイル送信(画像・PDFなど)

  • バイナリデータを送信する場合、application/x-www-form-urlencoded では不向き
  • 代わりに Content-Type: multipart/form-data が使用される

📋 送信例(抜粋)

POST /upload HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryabc123

------WebKitFormBoundaryabc123
Content-Disposition: form-data; name="file"; filename="image.jpg"
Content-Type: image/jpeg

(binary image data)
------WebKitFormBoundaryabc123--

🎯 攻撃者が悪用するポイント

✅ multipart/form-dataの特徴

  • ファイル名やContent-Typeヘッダーを自由に設定できる
  • 攻撃者は filename="shell.php" + Content-Type: image/jpeg のように 拡張子とMIMEタイプを偽装できる

✅ サーバー側のよくあるミス

  • Content-Typeヘッダーを信じて画像ファイルと判定 → 実際にはPHPスクリプト
  • 拡張子チェックとContent-Typeチェックの組み合わせが中途半端

🛡️ 理解すべきポイント

  • multipart/form-data自体は 正常なファイルアップロード方式
  • 攻撃者は multipartの柔軟さ を利用して検証をバイパスしようとする

🎯 攻撃者の手口例

  1. filename=avatar.jpg.php
  2. Content-Type: image/jpeg
  3. 実体はPHPコード
  4. サーバー側がMIMEや拡張子チェックを誤ればアップロード成功
  5. /uploads/avatar.jpg.php?cmd=whoamiサーバー制御権奪取

✅ 開発者側の防御策

対策 説明
✅ MIMEだけでなくマジックバイトも確認 実ファイルのヘッダー情報を確認
拡張子+MIMEの両方検証 二重チェックでバイパスを防ぐ
実行不可ディレクトリにファイルを保存 仮にPHPをアップロードされても実行されない
ファイル名はサーバー側でランダム化 攻撃者の意図したファイル名を無効化

🎯 まとめ

  • multipart/form-dataは 便利だが攻撃者にも好都合
  • ブラウザとBurp Suite等の手動リクエストでは内容が変えられる
  • サーバー側の不十分なファイルタイプ検証は即座に突破される可能性がある

「標準機能=安全ではない!」 multipart/form-dataの仕組みを深く理解することで、 攻撃者の視点と守る側の視点が両方身につきます。

Best regards, (^^ゞ