フルスタックエンジニアのQuang Tranです。エンジニアとして4年間働くなかで、Amazon S3のようなクラウドストレージへのファイルアップロードには何度も苦労してきました。以前は、クライアントから受け取ったファイルをいったんサーバーに保存し、そこからS3に送る従来の方法を使っていました。一見シンプルな処理ですが、すぐにサーバーのリソースを圧迫するようになり、サーバーは過負荷で「悲鳴」を上げるようになりました。

そんなときに出会ったのが、サーバーに負荷をかけずにクライアントからS3へ直接ファイルをアップロードできる署名付きURL(Presigned URL)です。本記事では、署名付きURLの仕組みと実装方法をご紹介します。

従来のファイルアップロード

SNSに写真を投稿するなど、ファイルアップロード機能のあるアプリケーションでは、端末から写真を選択してサーバーに送信し、保存するという流れになります。従来のアップロード方式の手順は次のとおりです。

  1. ユーザーが端末から写真を選択する
  2. クライアントがサーバーに写真のアップロードをリクエストする
  3. サーバーが写真を受け取って処理し、ストレージに保存する
従来のファイルアップロードの流れ

従来のファイルアップロードの流れ

この流れはシンプルに見えますが、サーバーのパフォーマンスに影響を及ぼす可能性があります。数千人のユーザーが同時に大量のデータをアップロードすれば、サーバーは過負荷に陥りかねません。そのため、アプリケーションサーバーをスケールさせ、十分なネットワーク帯域を確保する必要が出てきます。

こうした課題への解決策としてAWSが提供しているのが、署名付きURLの機能です。では、署名付きURLとは何でしょうか。

署名付きURL(Presigned URL)とは

署名付きURLとは、特定のS3オブジェクトへの一時的なアクセス権をユーザーに付与するためのURLです。署名付きURLを使えば、サーバーを経由せずにS3のオブジェクトを直接読み取ったり、アップロードしたりできます。相手側にAWSのセキュリティ認証情報や権限がなくてもアップロードが可能です。なお、署名付きURLで指定したバケットに同じキーのオブジェクトがすでに存在する場合、Amazon S3は既存のオブジェクトをアップロードされたオブジェクトで置き換えます。

署名付きURLを作成する際には、次の情報を指定する必要があります。

  • Amazon S3のバケット名
  • オブジェクトキー(読み取りの場合はバケット内のオブジェクト、アップロードの場合はアップロードするファイル名)
  • HTTPメソッド(オブジェクトの読み取りはGET、アップロードはPUT)
  • 有効期限
  • AWS認証情報(アクセスキーID、シークレットアクセスキー)

署名付きURLは、有効期限が切れるまで何度でも使用できます。

Amazon S3は署名付きURLを通じてオブジェクトへのアクセスを許可します。署名付きURLを生成できるのは、バケットの所有者または有効なセキュリティ認証情報を持つユーザーに限られます。

署名付きURLを使ってS3にファイルをアップロードする流れは、次の図のとおりです。

署名付きURLを使ってS3にファイルをアップロードする流れ

署名付きURLを使ったファイルアップロードの流れ

アップロード用の署名付きURLを作成する方法

署名付きURLの概要がわかったところで、実際に作成して写真をアップロードする方法を見ていきましょう。

アップロード用の署名付きURLを作成する方法は、次の2つです。

  • AWS Toolkit for Visual Studio(Windows)を使う
  • AWS SDKを使って、ファイルアップロード用のPUT署名付きURLを生成する

本記事では、AWS SDK for JavaScriptを使ってPUT署名付きURLを生成する方法を紹介します。

AWS SDK for JavaScriptを使う

まず、S3オブジェクトの読み書き権限を持つアカウントでAWSマネジメントコンソールにログインします。

  • AWS SDKで署名付きURLを生成する場合、有効期限は作成から最長7日間です。
  • アップロードの前に、AWS認証情報(アクセスキーID、シークレットアクセスキー)、リージョン、S3バケット名、オブジェクトキーを準備し、サーバー側で安全に管理しておく必要があります。

署名付きURLの作成を始める前に、次の重要なポイントを押さえておきましょう。

  • S3バケットへのパブリックアクセスをすべてブロックする(データセキュリティの基本であり、意図しない情報漏えいや機密情報への不正アクセスを防ぎます)
  • AWS認証情報(アクセスキーID、シークレットアクセスキー)をフロントエンドのコードに絶対に保存しない
  • 環境変数やシークレット管理サービスを使って、AWS認証情報を安全に保管する
  • IAMの権限を必要最小限に絞る(AWSが推奨する最小権限の原則)
  • CORSを設定し、他のオリジンからのファイルアップロードリクエストを許可する

S3へ画像を直接アップロードする仕組みは、次の手順で構築します。

  1. フロントエンドから、バックエンドサーバーの署名付きURL生成APIを呼び出し、保存したいオブジェクトキーを送信します。
  2. バックエンドでは、次のように署名付きURLを生成するAPIを作成し、フロントエンドに返します。
1import {
2 PutObjectCommand,
3 S3Client,
4} from '@aws-sdk/client-s3';
5import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
6
7const createPresignedUrlWithClient = async ({ region, bucket, key }) => {
8 const client = new S3Client({
9 region,
10 credentials: {
11 accessKeyId: 'your access key id',
12 secretAccessKey: 'your secret key id',
13 },
14 });
15 const command = new PutObjectCommand({ Bucket: bucket, Key: key });
16 return await getSignedUrl(client, command, { expiresIn: 36000 });
17};
18
19const presignedUrl = await createPresignedUrlWithClient({
20 region: 'ap-southeast-1',
21 bucket: 'your-bucket-name',
22 key: 'example.txt',
23});

上記のコードは説明用に認証情報を直接記述していますが、実際の運用では環境変数やAWS Secrets Managerなどから読み込み、コードに認証情報を書かないようにしてください。

  1. フロントエンドはレスポンスを受け取り、PUTリクエストでS3バケットにファイルを直接アップロードします。
1const putToPresignedUrl = (presignedUrl) => {
2 const data = 'Hello World!';
3 axios.put(presignedUrl, data);
4};
アップロード後にS3バケットに保存されたオブジェクト

アップロード後にS3に保存されたオブジェクト

アップロードされたオブジェクトの内容

オブジェクトの内容

署名付きURLの例:

1https://presignedurldemo.s3.ap-southeast-1.amazonaws.com/example.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAUPMYNICO4HMDKONH%2F20250101%2Fap-southeast-1%2Fs3%2Faws4_request&X-Amz-Date=20250101T021742Z&X-Amz-Expires=36000&X-Amz-Signature=9f29f0f34a19c9e9748eb2fc197138d4345e0124746f99ad56e27e08886fa01a&X-Amz-SignedHeaders=host&x-amz-checksum-crc32=AAAAAA%3D%3D&x-amz-sdk-checksum-algorithm=CRC32&x-id=PutObject

S3は、URLに含まれるクエリパラメータをもとに、アップロード操作を許可するかどうかを判断します。主なパラメータは次のとおりです。

  • X-Amz-Algorithm:使用する署名アルゴリズム。通常はAWS4-HMAC-SHA256です。
  • X-Amz-Credential:アクセスキーIDとリクエストのスコープを含む文字列。形式は<AccessKey>/<Date>/<Region>/s3/aws4_requestで、リクエストの署名に使われた認証情報をAWSが識別するために使われます。
  • X-Amz-Date:URLが生成された日時(UTC)。形式はYYYYMMDD'T'HHMMSS'Z'です。
  • X-Amz-Expires:URLの有効期限までの秒数(例:1時間なら3600)。この時間を過ぎるとURLは無効になります。
  • X-Amz-SignedHeaders:署名に含まれるヘッダーの一覧。通常はhostのみですが、署名時に指定すればcontent-typeなども含められます。
  • X-Amz-Signature:実際の暗号署名。リクエストが改ざんされていないことを保証し、送信者が有効な認証情報を持っていることを証明します。

署名付きURLの生成方法がわかったところで、利用時に考慮すべき制約も確認しておきましょう。

S3署名付きURLの制約

  1. アップロード上限は5GB:S3では1回のリクエストでアップロードできるサイズが5GBまでに制限されており、簡単に引き上げる方法はありません。
  2. URL管理の手間:アップロードのたびに固有のURLを生成する必要があるため、コードやバックエンドのロジックが複雑になります。
  3. 意図しないアクセスのリスク:URLを知っていれば誰でも、有効期限が切れるまでアップロードできてしまいます。ユーザーを検証する仕組みは組み込まれていません。
  4. クライアント側アップロードの課題:アップロードの途中でエラーが発生すると、データの不整合が生じる可能性があります。

あわせて読みたい:

まとめ

本記事では、S3バケットを公開することなく、S3へオブジェクトを直接アップロードする方法を紹介しました。署名付きURLを使えばサーバーの負荷を抑えられる一方、5GBのアップロード上限やURL管理の手間といった制約もあります。ユースケースに合わせて最適な方法を選んでください。

参考資料:


出典:Uploading objects to AWS S3 with presigned URLs(SupremeTech、著者:Quang Tran)。日本語に翻訳して掲載しています。