WebVulnerabilities

ファイルアップロードによる攻撃について

ファイルアップロードの際の脆弱性は、Webアプリケーションがユーザーからアップロードされたファイルを適切に検証せずに処理することで、攻撃者が悪意のあるファイルをサーバーにアップロードし、不正な操作を行うことを可能にする脆弱性です。これにより、リモートコード実行、サーバーの完全制御、データ漏洩などの深刻なリスクが発生する可能性があります。

概要

ファイルアップロードに関する典型的な脆弱性には、以下のようなものがあります:

  1. 不正なファイル形式の受け入れ:
    • アップロードされたファイルの拡張子やMIMEタイプが不正であるにもかかわらず、適切に検証されずにサーバー上に保存され、サーバーで実行されるリスク。
  2. 任意の場所へのファイルアップロード:
    • 攻撃者がファイルの保存場所を操作し、サーバーの任意のディレクトリにファイルをアップロードするリスク。
  3. ファイル名の不正利用:
    • ファイル名に悪意のあるコードが含まれている場合、それがそのまま保存されたり、実行されたりするリスク。
  4. サイズの制限の不備:
    • アップロードファイルのサイズが不適切に制限されておらず、サーバーのリソースを過度に消費するリスク。

脆弱性を持つコードの例と攻撃手法

PHP

脆弱なコード例:

<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $upload_dir = '/var/www/uploads/';
    $upload_file = $upload_dir . basename($_FILES['file']['name']);
    if (move_uploaded_file($_FILES['file']['tmp_name'], $upload_file)) {
        echo "File successfully uploaded.";
    } else {
        echo "File upload failed.";
    }
}
?>

攻撃手法:

Python(Flask)

脆弱なコード例:

from flask import Flask, request

app = Flask(__name__)

@app.route('/upload', methods=['POST'])
def upload_file():
    file = request.files['file']
    file.save('/var/www/uploads/' + file.filename)
    return 'File successfully uploaded.'

if __name__ == '__main__':
    app.run()

攻撃手法:

Node.js(Express)

脆弱なコード例:

const express = require('express');
const multer = require('multer');
const upload = multer({ dest: '/var/www/uploads/' });
const app = express();

app.post('/upload', upload.single('file'), (req, res) => {
    res.send('File successfully uploaded.');
});

app.listen(3000);

攻撃手法:

Java(Spring)

脆弱なコード例:

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.stereotype.Controller;

import java.nio.file.Files;
import java.nio.file.Paths;

@Controller
public class FileUploadController {

    @PostMapping("/upload")
    public String uploadFile(@RequestParam("file") MultipartFile file) throws Exception {
        Files.write(Paths.get("/var/www/uploads/" + file.getOriginalFilename()), file.getBytes());
        return "File successfully uploaded.";
    }
}

攻撃手法:


防止策

  1. ファイルの種類と拡張子の検証:
    • アップロードされるファイルの種類と拡張子を厳密に検証し、許可されたファイルタイプ(例えば画像のみなど)のみを受け入れるようにします。

    PHPの例:

    $allowed_types = array('image/jpeg', 'image/png');
    $file_type = mime_content_type($_FILES['file']['tmp_name']);
    if (!in_array($file_type, $allowed_types)) {
        die("Invalid file type.");
    }
    

    Pythonの例(Flask):

    allowed_extensions = {'jpg', 'jpeg', 'png'}
    def allowed_file(filename):
        return '.' in filename and \
               filename.rsplit('.', 1)[1].lower() in allowed_extensions
    
  2. ファイル名のサニタイズ:
    • アップロードされたファイル名をサニタイズし、任意のファイル名を使用できないようにします。さらに、サーバー上でのファイル保存時には、ランダムなファイル名を生成して使用することを推奨します。

    Node.jsの例(Express):

    const path = require('path');
    const sanitizedFilename = path.basename(req.file.originalname);
    
  3. アップロード先ディレクトリの適切な設定:
    • アップロードされたファイルは、Webサーバーのルートディレクトリ外に保存し、Webから直接アクセスできない場所に保存することで、悪意のあるファイルが実行されないようにします。

    Javaの例(Spring):

    String savePath = "/safe/uploads/" + file.getOriginalFilename();
    Files.write(Paths.get(savePath), file.getBytes());
    
  4. サイズ制限の実装:
    • アップロードされるファイルのサイズを制限し、大量のデータをサーバーにアップロードしてリソースを消費する攻撃を防ぎます。

    PHPの例:

    if ($_FILES['file']['size'] > 1048576) { // 1MBを超えるファイルを拒否
        die("File is too large.");
    }
    
  5. セキュリティ対策済みのライブラリの使用:
    • 可能であれば、ファイルアップロード処理のためのセキュリティ対策が実装されているフレームワークやライブラリを使用することが推奨されます。

まとめ: ファイルアップロードは非常に便利な機能ですが、適切なセキュリティ対策が施されていないと、深刻な脆弱性を引き起こす可能性があります。ファイルの種類の検証、ファイル名のサニタイズ、適切なディレクトリ設定、サイズ制限の実装などの防止策を適用することで、これらのリスクを軽減することができます。