安全でないファイルアップロード

C#の安全でないファイルアップロード

説明

アップロードされたファイルを検証しなかったり、その内容をHTMLとして配信したりすると、攻撃者がスクリプトを含むファイルをアップロードして実行させるおそれがあります。

想定される影響

  • 保存型XSS、悪意のあるファイルの配布、コンテンツタイプの混同につながる可能性があります。

対処方法

ファイルのサイズ、拡張子、MIMEタイプ、保存場所を検証し、アップロードされたファイルは安全なコンテンツタイプで配信してください。

例

変更前

csharp
using Microsoft.AspNetCore.Mvc;

public sealed class UploadController : Controller
{
    public IActionResult View(string id)
    {
        var fileBytes = System.IO.File.ReadAllBytes("/tmp/uploads/" + id);
        return File(fileBytes, "text/html");
    }
}

変更後

csharp
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.IO;
using System.Threading.Tasks;

public sealed class UploadController : Controller
{
    public async Task<IActionResult> Upload([FromForm] IFormFile file)
    {
        if (file.Length is <= 0 or > 1_048_576) return BadRequest();
        if (!string.Equals(
                Path.GetExtension(file.FileName), ".txt",
                StringComparison.OrdinalIgnoreCase)) return BadRequest();

        // このパスと親ディレクトリには、サービスだけが書き込めるようにしてください。
        var uploadRoot = Path.GetFullPath("/var/lib/app/uploads");
        Directory.CreateDirectory(uploadRoot);
        var storedName = $"{Guid.NewGuid():N}.txt";
        var destination = Path.Combine(uploadRoot, storedName);
        await using var output = new FileStream(
            destination, FileMode.CreateNew, FileAccess.Write, FileShare.None);
        await file.CopyToAsync(output);
        return Accepted(new { id = storedName });
    }
}

解説:

  • 変更前: アップロードされたファイルを検証せずに保存し、HTMLとして配信すると、含まれるスクリプトがブラウザーで実行されるおそれがあります。
  • 変更後: サイズと許可する拡張子を制限し、クライアントのファイル名の代わりにサーバーが生成した名前を使って、Webルートの外に新しいファイルだけを作成します。実際のサービスでは内容に基づく形式の検証とマルウェア検査も追加し、ダウンロード時に実行可能なHTMLのMIMEタイプで配信しないでください。

参考資料