見えるセキュリティ

ファイルアップロードの脆弱性と対策

FILE UPLOAD SECURITY

この概念とは?

画像や文書を受け取る機能では、ファイル名だけで中身を信用してはいけません。想定外のファイルを受け付けると、その後の処理や公開方法によって、不正な動作や情報漏えい、サービス停止につながる恐れがあります。サーバー側で内容を確かめ、保存方法・容量・権限などの対策を組み合わせます。[T01・T02・T07]

.pngという名前なのに、なぜPNG画像として受け付けてはいけないのか?

THEME
web-vuln
KIND
defense
REVIEW
self-reviewed ·

この機能はPNG画像だけを受け付けます。同じファイルを送り、中身を確かめるかどうかを切り替えます。

図を見る前に:拡張子・申告型・先頭識別値
拡張子
photo.pngの.pngの部分。名前を変えても中身は変わりません。
申告Content-Type
送信側が付けるimage/pngなどの種類の情報。受信側が中身を検査した結果ではありません。[T01・T04]
先頭識別値
形式の目印になる、ファイル先頭の決まったバイト列。PNGでは8バイト。電子署名・SRIのハッシュとは別であり、暗記は不要です。[T03]

ファイル名

photo.png

実際の中身(文字データ)

NOT PNG + 改行

説明者の注記です。アプリの検査結果とは区別します。

送信側の申告:image/png8バイト受信済み

サーバー側アプリPNG画像だけを登録する機能

同じファイルを受信

利用者

photo.png

送信の再現

受信データ

photo.png

受信済み。登録用の保存は、まだです。

同じ名前・申告型・中身を受け取っています。

登録用の保存先

未登録

受信と登録用保存は別です。元ファイル・受信データは残ります。実際のアップロード・保存・公開・実行は行いません。

両モード共通:認証・アップロード権限あり/容量条件は制限内。拡張子は未確認。中身は未到達。登録用保存はなし。受付結果は結果前です。

.pngという名前やimage/pngという申告だけでは、PNG画像と確認したことにはなりません。この例では、実データの先頭がPNGの決まりと違うため、登録用の保存前に拒否できます。ただし、先頭が一致するだけで安全とは判断できません。[T01・T03・T04]

STEP 01 / 5

同じファイルを受け取る

STEP 1 / 5: 同じファイルを受け取る

現在のSTEP 1STEP 2STEP 3STEP 4STEP 5

photo.pngを受信しました。送信側は種類をimage/pngと申告しています。登録用の保存はまだです。

技術的な詳細を見る

実データはASCIIのNOT PNGと末尾のLF、合計8バイトです。image/pngはファイルpartの申告Content-Typeで、要求全体のmultipart/form-dataとは別です。受信データはメモリ・ディスク・ストリーム等で扱われ、一律に一時ファイルとは限りません。[T01・T04]

パネルの開閉ではSTEPは変わりません。

この概念の要点

  • 要点

    名前・申告型・実データは別の情報

    .pngやimage/pngだけで中身を確認したことにはならない。サーバー側で受入条件を確かめる。[T01・T04・T05]

  • 要点

    何と比べた結果、どこで止めるか

    この例ではPNGに必要な先頭値と実データを比べ、不一致なので登録前に拒否する。一致は追加検査へ進む手掛かりであり、安全の証明ではない。[T01・T03]

  • 要点

    保存・利用まで対策を組み合わせる

    保存先・保存名、実行や公開の制御、容量、認可なども設計する。受信、登録用保存、公開、実行は別の段階である。[T01・T02・T07]

理解確認

主図のファイルをPNGとして拒否できる根拠はどれですか?

理解確認

PNGの先頭8バイトと一致した場合、どう考えるべきですか?

理解確認

アップロード後の扱いについて適切なのはどれですか?

ファイルアップロードの補足

拡張子・申告型・中身を分ける

ファイル名を変更しても、内容が別の形式へ変換されるわけではありません。送信時のContent-Typeも送信側が付ける情報です。ブラウザの選択欄で種類を絞るaccept属性は選択を助けるヒントであり、サーバー側の検査の代わりにはなりません。[T01・T04・T05]

PNGの先頭8バイトのような目印は、形式を見分ける手掛かりです。ただし、先頭の値だけ一致しても、残りのデータがPNGの構造を満たすとは限りません。信頼できる処理ライブラリで形式・構造を確認し、扱う用途に必要な条件を満たすか調べます。[T01・T03]

先頭一致だけでは受理できない
教材用の例先頭の照合ここで言えること
NOT PNG+LFの8バイト不一致PNGの必須条件を満たさず、登録対象にしない
PNGの目印8バイトだけのデータ一致画像本体や必要な構造がなく、これだけではPNGとして受理できない

目印だけのデータ:画像ファイルの成功例ではありません。

89504E470D0A1A0A

ファイルpartのヘッダー例

Content-Disposition: form-data; name="file"; filename="photo.png"
Content-Type: image/png

これは要求全体のContent-Typeではありません。multipartはメッセージ形式であり、安全性検査そのものではありません。[T04]

保存名と保存場所をサーバー側で決める

利用者が送った名前を、そのままサーバーの保存先にしないようにします。アプリ側で保存名を生成し、保存先を限定し、同名ファイルの上書きや対象外への書き込みを防ぎます。元の名前を表示する必要があれば、保存用の識別子とは分けて扱います。[T01・T02・T04]

受信名 photo.png → アプリが付ける識別子 → 限定した登録用保存先

アップロード用の保存先は、可能なら別サーバーやWebの直接公開領域の外へ置きます。必要なファイルだけをアプリの処理を通して返し、取得する利用者の認可も確認します。名前をランダムにすることや、保存先を分けることだけで、認可まで完了するわけではありません。[T01・T02]

関連:ディレクトリトラバーサル(パストラバーサル)
保存しただけでは実行されないが、利用方法に注意する

アップロードしたファイルが、保存しただけで必ず動くわけではありません。しかし、保存先をWebサーバーがスクリプトとして処理したり、公開した内容をブラウザが解釈したり、後段の処理ライブラリに弱点があったりすると、被害につながる場合があります。実行や公開・処理の方法まで含めて制御します。[T02・T07]

サーバー側の処理
保存先を処理系が解釈する経路や、後段の処理ライブラリの弱点に注意します。
閲覧側の解釈
公開した内容をブラウザが解釈する経路に注意します。

ファイルの実行権限を外すことだけで、あらゆる処理系による解釈が止まるとは限りません。利用目的に合う配信方法と権限を設定し、処理ライブラリを更新します。検査・画像の再生成・マルウェア検査なども、必要に応じて組み合わせます。[T01・T07]

容量・処理量・権限も制限する

形式が合っていても、大量のファイルや大きな画像で保存容量・メモリ・CPUを消費することがあります。受信サイズ、件数・頻度、展開後の容量や画像の画素数など、用途に応じた制限を設けます。認証・認可やCSRF対策も必要です。[T01・T07]

主図は、小さいファイルを送る権限のある利用者という条件です。実環境では、ファイルの形式だけに頼らず、受信・検査・保存・利用の段階を通して対策します。[T01]

形式と拡張子の整合確認は、令和7年度秋期の情報処理安全確保支援士試験でも問われています。ここでは独自のPNG例を使い、今後の出題を保証するものではありません。[T06]

参考資料

RELATED CONCEPTS