佐賀人のIT技術者のブログ

IT技術、日常のブログです。たまに地元・佐賀について書きます。

Flask初心者向け!安全なファイルアップロードと元の名前でのダウンロード完全ガイド

Flaskを使用してファイルを安全にアップロードし、元の名前でダウンロードできる機能を実装する方法をご紹介します。この方法は、werkzeug.utilsモジュールのsecure_filenameを活用して、セキュリティを考慮したファイル名の管理を実現します。

目次

  1. Flaskでのファイルアップロードの仕組み
  2. 安全なファイル名を作るsecure_filenameの役割
  3. ファイルアップロードとダウンロードの具体的な実装
  4. 実行例と注意点

1. Flaskでのファイルアップロードの仕組み

Flaskでは、request.filesを使用してクライアントから送信されたファイルを取得できます。取得したファイルはfile.save(path)メソッドでサーバーに保存可能です。ただし、ユーザーが指定したファイル名をそのまま使うと、セキュリティ上のリスクがあります。

例えば、「../../etc/passwd」のような名前のファイルをアップロードされると、サーバー内部のディレクトリ構造にアクセスされる可能性があります。これを防ぐために、secure_filenameで安全なファイル名に変換することが重要です。

2. 安全なファイル名を作るsecure_filenameの役割

secure_filenameは、ファイル名を安全な形式に変換してくれる便利な関数です。以下のように、特殊文字ディレクトリトラバーサル攻撃を防ぎます。

変換例

元のファイル名 変換後のファイル名
../../etc/passwd etc_passwd
hello world!.jpg hello_world.jpg
my<script>.png my_script_.png

3. ファイルアップロードとダウンロードの具体的な実装

必要なモジュールをインポート

以下のモジュールをインポートします。

from flask import Flask, request, send_from_directory, jsonify
from werkzeug.utils import secure_filename
import os

サーバーの基本設定

保存先のフォルダを指定し、初期設定を行います。

app = Flask(__name__)
UPLOAD_FOLDER = 'uploads'
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
os.makedirs(UPLOAD_FOLDER, exist_ok=True)

ファイルアップロードの実装

ファイルをアップロードし、安全な名前に変換して保存する処理を実装します。

@app.route('/upload', methods=['POST'])
def upload_file():
    if 'file' not in request.files:
        return jsonify({"error": "No file part"}), 400
    
    file = request.files['file']
    if file.filename == '':
        return jsonify({"error": "No selected file"}), 400
    
    original_filename = file.filename
    secure_name = secure_filename(original_filename)
    save_path = os.path.join(app.config['UPLOAD_FOLDER'], secure_name)
    
    file.save(save_path)
    return jsonify({
        "message": "File uploaded successfully",
        "original_name": original_filename,
        "saved_name": secure_name
    }), 200

ファイルダウンロードの実装

アップロードされたファイルを元の名前でダウンロードする処理を実装します。

@app.route('/download/<filename>', methods=['GET'])
def download_file(filename):
    secure_name = secure_filename(filename)
    file_path = os.path.join(app.config['UPLOAD_FOLDER'], secure_name)
    
    if not os.path.exists(file_path):
        return jsonify({"error": "File not found"}), 404
    
    return send_from_directory(
        app.config['UPLOAD_FOLDER'],
        secure_name,
        as_attachment=True,
        download_name=filename  # Flask 2.0以降
    )

4. 実行例と注意点

実行例

ファイルアップロード

curl -X POST -F "file=@example.txt" http://localhost:5000/upload

ファイルダウンロード

curl -O http://localhost:5000/download/example.txt

注意点

  • ファイル名の管理: サーバーに保存された名前と元の名前のマッピングが必要な場合は、データベースやJSONファイルを利用するのがおすすめです。
  • セキュリティ: secure_filenameを必ず使用して、不正なファイル名を排除しましょう。

まとめ

Flaskでのファイルアップロードとダウンロードを実装する際、secure_filenameを活用することでセキュリティを向上させることができます。また、ユーザーにとって使いやすい元の名前でのファイルダウンロードも簡単に実現できます。

ぜひこの方法を活用して、安全で便利なファイル操作機能を作成してみてください!