【実務・中級編】アップロードされたファイルの実行を防ぐディレクトリ設定 – アプリケーションセキュリティ & 安全な開発防御ガイド

攻撃者が「アップロード」の瞬間に何を考えているか:ファイル実行の封じ込め戦略

現場のインシデント対応をしていると、決まって遭遇するのが「ファイルアップロード機能の悪用」だ。攻撃者は、ユーザープロフィール画像やCSVインポート機能の裏に、密かにPHPやPythonのシェルコードを潜ませる。

「アップロード先ディレクトリのパーミッションを755にしておけば大丈夫だろう?」なんて考えは、今すぐ捨ててくれ。それは、泥棒に対して「玄関の鍵は開いているが、リビングには入るな」と言っているのと同じだ。

今日は、Webサーバーがアップロードされたファイルを「ただのデータ」として扱い、決して「実行可能なプログラム」として解釈させないための、鉄壁の守り方を伝授する。

—

1. 攻撃者が狙う「盲点」:PoCの現実

攻撃者が狙うのは、サーバー側の設定ミスだ。例えば、ApacheのAddHandlerやNginxのfastcgi_script_nameの設定が甘いと、image.jpg.phpのようなファイルがアップロードされた瞬間に、サーバーがそれをPHPとして実行してしまう。

もし、アップロードディレクトリに実行権限が残っていれば、攻撃者はブラウザからそのURLに直接アクセスするだけで、Webシェル(コマンド実行インターフェース)を起動し、サーバー内の機密情報やDBへ自由にアクセスできるようになる。これを防ぐには、「そもそもサーバーがそのディレクトリでプログラムを動かすことを拒否する」設定が必要だ。

—

2. Nginxにおける「実行禁止」の堅牢な設定

Nginxを使っているなら、特定のディレクトリ配下でのPHP実行を完全に遮断するのが定石だ。単純にlocationブロックを書き換えるだけでいい。

アップロードディレクトリ(/uploads/)への直接アクセス制御
location /uploads/ {
# 1. PHPなど、サーバーサイドスクリプトの実行を無効化
location ~ \.(php|php7|py|pl|sh|cgi)$ {
deny all;
return 403;
}

# 2. 静的ファイル以外は配信しない(拡張子ホワイトリスト)
location ~ \.(jpg|jpeg|png|gif|pdf|txt)$ {
# キャッシュの制御やセキュリティヘッダー付与
add_header X-Content-Type-Options “nosniff”;
}

# 上記以外のファイルはすべて拒否
deny all;
}

この設定の肝は、deny allでデフォルトを拒否しつつ、必要な静的ファイルだけをホワイトリスト方式で許可している点だ。これなら、未知の拡張子のファイルが紛れ込んでも実行されることはない。

—

3. アプリケーション層での多層防御(PHPの実装例)

サーバー設定をいじれる権限がない場合や、多層防御を徹底したい場合は、アプリケーション側で「ファイル名」と「中身」を徹底的に検閲する必要がある。

  • 安全なファイル保存のためのガード関数
  • /
    function save_uploaded_file(array $file, string $upload_dir): bool {
    // 1. MIMEタイプの検証(拡張子だけで判断しない)
    $finfo = new finfo(FILEINFO_MIME_TYPE);
    $mime = $finfo->file($file[‘tmp_name’]);
    $allowed_mimes = [‘image/jpeg’, ‘image/png’, ‘application/pdf’];

    if (!in_array($mime, $allowed_mimes, true)) {
    throw new Exception(“不正なファイルタイプです。”);
    }

    // 2. ファイル名をランダムなハッシュ値に置換(攻撃者の名前を無効化)
    $extension = pathinfo($file[‘name’], PATHINFO_EXTENSION);
    $new_name = bin2hex(random_bytes(16)) . ‘.’ . $extension;

    // 3. 安全なパスへ移動
    return move_uploaded_file($file[‘tmp_name’], $upload_dir . DIRECTORY_SEPARATOR . $new_name);
    }

    ここで重要なのは、「ユーザーが送ってきたファイル名をそのまま使わない」ことだ。filename.phpという名前でアップロードされたら、サーバー上ではa7b8c9...d0e.jpgのように、意味のない名前に強制変換する。これでディレクトリトラバーサル攻撃やOSコマンドインジェクションの芽を摘むことができる。

    —

    4. プロの運用ルール:クラウドストレージの活用

    最後に、もしあなたがAWSやGCPなどのクラウド環境を使っているなら、「Webサーバーと同じローカルディスクにファイルを保存するな」と強く言いたい。

    ファイルをS3やGCSのようなオブジェクトストレージに逃がすのが、現代のセキュリティの最適解だ。

    • 実行権限の概念がない: S3のバケットにPHPファイルを置いても、S3はそれを実行しない。
    • IAMによるアクセス制御: アプリケーション側からは「アップロード用の一時的URL」を発行するだけで、サーバーが直接ファイルを触る必要がなくなる。

    まとめ:セキュリティは「諦め」の積み重ね

    セキュリティエンジニアの仕事は、攻撃を全て見抜くことではなく、「もし攻撃者が中に入ったとしても、できることを極限までゼロにする」ことだ。

    1. アップロード先ディレクトリでは、スクリプト実行を明示的に禁止する(Nginx/Apache設定)。
    2. 保存するファイル名は必ずランダム化し、元ファイル名はDBにのみ保持する。
    3. 可能であれば、Webサーバーとファイルストレージを物理的に(論理的に)分離する。

    この3つを徹底するだけで、インシデントの確率は劇的に下がる。今日から君たちのチームでも、この設定を見直してみてほしい。現場からは以上だ。何かあればいつでも相談してくれ。

    コメント

    タイトルとURLをコピーしました