ファイルアップロード機能の脆弱性と対策。Webシェルを置かせないための検証と保存の設計
対象の目安: Webアプリ開発者 / 実務

プロフィール画像や添付書類、CSVの取り込みなど、ファイルを受け取る機能はWebアプリのあちこちに登場します。受け取ったファイルをそのまま公開ディレクトリに保存し、拡張子やContent-Typeだけを軽く見て通してしまうと、攻撃者はサーバ上で動くプログラムを送り込めます。PHPやJSPのような、Webサーバが自動的に解釈して実行する形式のファイルが置かれた瞬間、それはリモートからのコード実行(RCE)への入口になります。
この記事は、ファイルを受け取るコードを書くWebアプリ開発者に向けて、アップロード機能がなぜ狙われるのかという悪用の機構から、入口での検証で見るべきこと、そして受け取ったファイルを実行させない保存と配信の設計までを順に整理します。これはCWE-434「Unrestricted Upload of File with Dangerous Type(危険な種類のファイルの無制限なアップロード)」として知られる問題で、入力の検証という一般原則とは別に、アップロード固有の落とし穴を扱います。
なぜアップロード機能が狙われるのか
アップロード機能の危うさは、外部から受け取ったファイルが、サーバ上で単なるデータにとどまらず「実行される」「解釈される」余地を持つ点にあります。悪用のされ方はいくつかの型に分かれます。
もっとも影響が大きいのがWebシェルの設置です。攻撃者は.phpや.jsp、.aspといった、Webサーバが自動で実行する形式のファイルをアップロードし、そのURLへアクセスして中身のコードを走らせます。OWASPのコミュニティ資料は、Webシェルをアップロードして実行されるとサーバが乗っ取られ、コマンドの実行やシステムファイルの閲覧、内部リソースへの探索、他サーバへの攻撃の踏み台化にまで至ると説明しています。CWE-434も、アップロードされたファイルが受け手の環境でコードとして解釈、実行されると任意コード実行が成立し得るとしています。
次に保存型XSSです。HTMLファイルやSVG画像、JavaScriptを含むファイルをアップロードでき、それがブラウザにHTMLとして表示される経路があると、閲覧した利用者のブラウザで攻撃者のスクリプトが動きます。SVGは画像でありながら内部に<script>を書ける形式のため、画像として許可したつもりでもスクリプトの実行口になります。
ファイル名の扱いも見落とされがちです。受け取ったファイル名をそのまま保存パスに連結すると、../を含む名前によって想定した保存先の外へファイルを書き込まれるパストラバーサルが起こります。既存ファイルの上書きや、公開ディレクトリの意図しない場所への配置を許すことになります。
このほか、過大なサイズや大量のファイルを送りつけてディスクや処理を枯渇させるDoS、圧縮ファイルを展開させて巨大化させる攻撃、そして画像として正しく開けながら別の形式としても解釈されるポリグロット(複数形式を兼ねるファイル)による検証の擦り抜けも想定します。共通するのは、外部が用意したファイルの中身と、その置き場所をサーバ側が制御しきれていない点です。
入口の検証で見るべきこと
入口の検証は、明らかに不正なファイルを早い段階で拒否する一段目の壁です。ただし後述するとおり、これだけを頼りにはできません。まずは検証で押さえる項目を整理します。
拡張子は許可リスト(allowlist)で判定します。OWASPのFile Upload Cheat Sheetは、業務に必要な安全な拡張子だけを許可し、危険なものを列挙して弾く拒否リストではなく許可リストで組むことを勧めています。「危険な拡張子を消す」発想は、.php5や.phtmlのような別表記、大文字小文字の混在、file.php.jpgのような二重拡張子で回避されます。CWE-434も、二重拡張子の防止と、大文字小文字を区別しない環境での判定を明記しています。
拡張子だけでは中身を保証できないため、ファイルの実体を検証します。多くの形式は先頭に固有のバイト列(マジックバイト、シグネチャ)を持ちます。PNGなら先頭が89 50 4E 47、JPEGならFF D8 FFといった具合です。宣言された拡張子と実体のシグネチャが一致するかを確かめることで、拡張子だけを書き換えた偽装を弾けます。
そして、Content-Typeヘッダとファイル名は信用しません。OWASPは、Content-Typeは詐称できるため信用せずファイルの種類を検証せよと述べ、コミュニティ資料もWebプロキシでリクエストヘッダのContent-Typeを書き換えるだけで容易に回避できると指摘しています。ファイル名も同様に攻撃者が自由に決められる文字列であり、それを保存パスや表示にそのまま使ってはいけません。
注意
マジックバイトの一致は「その形式として妥当」を示すだけで、「安全」を保証しません。正しいPNGヘッダを持ちながら別の形式としても解釈されるポリグロットや、画像の中にコードを潜ませたファイルが通り得ます。入口の検証は擦り抜けられる前提で、後段の「実行させない保存」を必ず併用してください。
サイズの上限も入口で設けます。OWASPはファイルサイズの上限設定を挙げており、過大なファイルによる枯渇を防ぎます。圧縮ファイルを扱う場合は、展開後のサイズを安全に見積もってから展開します。入口検証の考え方は、入力全般の検証原則と地続きです。役割分担は別記事で整理しています。
あわせて読みたい
入力バリデーションと出力エスケープの原則。入口で検証し、出口で文脈別にエスケープする
実行させない保存と配信の設計
入口の検証をすり抜けたファイルがあっても被害に至らせない、というのが対策の要です。要点は、受け取ったファイルをサーバに実行させない、解釈させないことにあります。
- 1
公開ディレクトリの外またはオブジェクトストレージに保存する
アップロードされたファイルを、Webサーバが直接配信する公開ディレクトリ(webroot)の下に置かないようにします。OWASPは、可能なら別サーバに、それが難しければwebrootの外に保存することを勧めています。オブジェクトストレージに預け、配信は専用の経路を通す形も有効です。URLを推測してアクセスすれば実行、という状態を作らないことが狙いです。
- 2
保存名をサーバ側でランダム生成する
元のファイル名は使わず、UUIDなどサーバ側で生成した名前で保存します。CWE-434とOWASPはいずれも、利用者が指定した名前ではなくアプリが生成した一意な名前を使うよう挙げています。これでファイル名によるパストラバーサルや、拡張子の細工を保存の段階で断てます。
- 3
アップロード先でのスクリプト実行を無効化する
保存ディレクトリでは、Webサーバやアプリ実行環境がスクリプトを解釈しない設定にします。実行権限を外し、そのパスではPHPなどのハンドラを無効化します。万一実行形式が保存されても、コードとして走らない状態を作ります。
- 4
配信時にContent-Dispositionを付与する
ファイルを利用者へ返すときは、
Content-Disposition: attachmentを付けてブラウザに表示ではなくダウンロードとして扱わせ、HTMLやSVGがその場でレンダリングされてスクリプトが走るのを防ぎます。あわせてContent-Typeを明示し、X-Content-Type-Options: nosniffでブラウザ側の推測解釈を抑えます。 - 5
画像は再エンコードして無害化する
画像は、受け取ったバイト列をそのまま保存せず、サーバ側で読み込んで再エンコード(書き出し直し)します。OWASPは画像の書き換えによって注入された悪意ある内容を除去できるとしており、メタデータやポリグロット的な余分なデータを落とせます。
さらに、アップロードと取得の認可を設計します。誰がアップロードでき、誰がそのファイルを取得できるのかを認証と認可で制御し、想定外の利用者が任意のファイルへ到達できないようにします。運用の防御として、受け取ったファイルをマルウェアスキャンにかける、可能ならCDR(Content Disarm and Reconstruction)で無害化する、といった多層の備えも有効です。ファイル名を保存パスへ連結する処理を残す場合は、パストラバーサルへの対策も併せて確認してください。
あわせて読みたい
パストラバーサルとは何か。基準ディレクトリの外へ解決されるパスの脆弱性と根本対策を解説
対策チェックリスト
ファイルアップロード対策チェックリスト
- 拡張子を許可リスト(業務に必要な形式だけ)で判定しているか
- 拡張子だけでなく、マジックバイトによる実体の検証を重ねているか
- Content-Typeヘッダとファイル名を信用せず、保存やパスにそのまま使っていないか
- 二重拡張子(file.php.jpg 等)や大文字小文字、ポリグロットを想定しているか
- ファイルサイズの上限を設け、圧縮ファイルは展開後サイズを見積もっているか
- 公開ディレクトリ(webroot)の外、またはオブジェクトストレージに保存しているか
- 保存名をサーバ側でランダム生成し、元のファイル名を使っていないか
- アップロード先でスクリプト実行を無効化しているか(実行権限、ハンドラ)
- 配信時にContent-Dispositionとnosniffを付け、その場で解釈させていないか
- 画像を再エンコードして無害化しているか
- アップロードと取得の認可、マルウェアスキャンを設計しているか
まとめ
ファイルアップロードの脆弱性は、外部が用意したファイルをサーバが実行、解釈してしまうことで成立します。拡張子の許可リストとマジックバイトの検証、Content-Typeとファイル名を信用しない姿勢は、明らかな不正を早く弾く一段目の壁として役立ちます。ただし入口の検証は擦り抜けられる前提で考えるべきで、要は公開ディレクトリの外へランダム名で保存し、その場所でスクリプトを実行させず、配信時にはダウンロードとして扱わせる、という実行させない保存と配信の設計にあります。CWE-434やOWASPが示す管理策も、この二段構えを土台にしています。OWASP Top 10でも、アクセス制御やインジェクションと並んで、外部入力を信頼しすぎないことが繰り返し説かれています。
出典・参考
関連する記事
パストラバーサルとは何か。基準ディレクトリの外へ解決されるパスの脆弱性と根本対策を解説
ユーザー入力がファイルパスに連結され「../」で基準ディレクトリの外へ到達されるパストラバーサル(CWE-22)を、なぜ成立するのかという機構から、任意ファイル読取などの想定影響、正規化と許可リストによる根本対策までWebアプリ開発者向けに体系的に整理します。
OSコマンドインジェクションの仕組みと根本対策。シェルに渡さず引数を分離する
OSコマンドインジェクション(CWE-78)が外部入力をシェルコマンドとして解釈させて成立する機構、OWASPのインジェクション分類での位置づけ、そしてシェルを介さず引数を配列で渡す根本対策までを開発者向けに整理します。入力検証が補助にとどまる理由も扱います。
入力バリデーションと出力エスケープの原則。入口で検証し、出口で文脈別にエスケープする
セキュアコーディングの土台である入力バリデーションと出力エスケープを、それぞれの役割の違いから整理します。入口の検証は防御の一段目、出口の文脈別エスケープがXSSやインジェクションの根本対策である理由を、原理と実務の判断基準まで掘り下げます。


