記事

S3の署名付きURL: 仕組み、有効期限の上限、コード例

S3の署名付きURLとは何か、各X-Amzパラメーターの意味、7日間の有効期限の上限、アップロードとダウンロードのコード(CLI、boto3、JS)、よくあるエラーの対処法を解説します。

S3の署名付きURLは、非公開バケット内の1つのオブジェクトへの一時的なリンクです。クエリ文字列に署名が含まれているため、リンクを持っている人はAWSの認証情報なしで、リンクの有効期限が切れるまでそのオブジェクトをダウンロード(またはアップロード)でき、バケットが公開されることはありません。署名付きURLはご自身のキーを使ってコードやAWS CLIで作成し、有効期間は1秒から7日間です。このガイドでは、その仕組み、各 X-Amz-* パラメーターの意味、有効期間、ダウンロードとアップロードのコード、そして遭遇しやすいエラーを説明します。制限は2026年10月時点のAWSとCloudflareのドキュメントに基づいています。

S3の署名付きURLの概要

質問回答
何を許可するか1つのオブジェクトに対する1つの操作(GET、PUT、HEAD、DELETEなど)
有効期限の上限SigV4とIAMユーザーのキーで7日間(604,800秒)
一時的なキーでの有効期限ロールのセッションまたはSTSの認証情報が終了した時点のいずれか早いほう
S3コンソールでの範囲1分から12時間
デフォルトの有効期限AWS CLIとboto3では3,600秒、JavaScript v3のpresignerでは900秒
複数回使えるかはい、有効期限まで誰でも何度でも使えます
1つのURLだけを取り消せるかいいえ、署名した認証情報を取り消すか、ポリシーで拒否するしかありません
作成時にS3を呼び出すかいいえ、署名はローカルで計算されます

署名付きURLの仕組み

  1. コード(またはCLI)が、バケット、オブジェクトキー、HTTPメソッド、有効期限を受け取ります。
  2. SDKが、AWS Signature Version 4を使い、シークレットアクセスキーでそれらの値に署名します。この時点ではS3に何も送信されません。
  3. 生成されたURLを、ブラウザー、同僚、モバイルアプリなどに渡します。
  4. URLが使われると、S3はURLに含まれるアクセスキーに対応するシークレットで署名を再計算します。署名が一致し、URLの有効期限が切れておらず、署名者にその操作の権限があれば、リクエストが処理されます。

URLには署名した人の権限が引き継がれます。AWSはこの点を明確にしています。URLを生成したIAMユーザーやロールがオブジェクトを読み取れない場合、署名がどれだけ有効でもURLは403を返します。

X-Amzパラメーターの意味

SigV4の署名付きURLは次のようになります(改行は追加したもの):

https://my-bucket.s3.eu-west-1.amazonaws.com/reports/q3.pdf
  ?X-Amz-Algorithm=AWS4-HMAC-SHA256
  &X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20261017%2Feu-west-1%2Fs3%2Faws4_request
  &X-Amz-Date=20261017T093000Z
  &X-Amz-Expires=3600
  &X-Amz-SignedHeaders=host
  &X-Amz-Signature=5f2c...e91a
パラメーター意味
X-Amz-Algorithm署名アルゴリズム: SigV4では AWS4-HMAC-SHA256
X-Amz-CredentialアクセスキーIDとスコープ: <key-id>/<YYYYMMDD>/<region>/s3/aws4_request
X-Amz-DateURLが署名された日時(UTC、yyyyMMddTHHmmssZ)
X-Amz-ExpiresX-Amz-Date からURLが有効な秒数(1から604800)
X-Amz-SignedHeaders署名の対象となるヘッダー。必ず host を含みます
X-Amz-Signature署名。16進数64文字
X-Amz-Security-Token一時的な認証情報(STS、ロール、EC2、ECS、SSO)で署名した場合にのみ存在します

署名された部分(キー、メソッド、有効期限、署名済みヘッダー)を1つでも変更すると、署名は一致しなくなります。また、URLが平文で何を明かしているかにも注意してください: バケット名、オブジェクトキー全体、アクセスキーIDです。シークレットキーがURLに含まれることはありません。

署名付きURLの有効期間

上限は、署名に使う認証情報によって異なります:

署名に使った認証情報最大有効期間
IAMユーザーのアクセスキー(SigV4)7日間。SigV4の署名キー自体が7日間有効なため
IAMロールのセッション(STS AssumeRole)セッションが終了するまで。デフォルトは1時間
EC2インスタンスプロファイルロールの認証情報がローテーションされるまで。最大約6時間
ECSタスクロールタスクの認証情報がローテーションされるまで。通常1〜6時間ごと
取り消しまたは削除された認証情報X-Amz-Expires の値にかかわらず即座に無効
S3コンソール1分から12時間の範囲で選択

これが最もよくある驚きの理由です。LambdaやEC2インスタンスから7日間を指定しても、リンクは1時間から数時間で使えなくなります。URLの寿命は、その背後にある認証情報の寿命と同じです。長期間のリンクが必要な場合は、そのバケットの読み取り権限だけを持つ専用のIAMユーザーで署名してください。

S3はリクエストの開始時に有効期限を確認します。期限の1秒前に始まった大きなダウンロードは通常どおり完了しますが、期限後の再試行は AccessDenied と「Request has expired」というメッセージで失敗します。604,800秒を超える値を指定すると、その時点で拒否されます。

署名付きURLの生成方法

AWS CLI

aws s3 presign s3://my-bucket/reports/q3.pdf --expires-in 3600

このコマンドはダウンロード(GET)用のURLに署名します。デフォルトは3,600秒、上限は604,800秒です。最近のCLIのバージョンは常にSigV4を使うため、リージョンが設定されていることを確認してください。アップロード用のURLにはSDKが必要です。

Python(boto3)

import boto3
from botocore.config import Config

s3 = boto3.client('s3', region_name='eu-west-1', config=Config(signature_version='s3v4'))

url = s3.generate_presigned_url(
    ClientMethod='get_object',
    Params={
        'Bucket': 'my-bucket',
        'Key': 'reports/q3.pdf',
        'ResponseContentDisposition': 'attachment; filename="q3.pdf"',
    },
    ExpiresIn=3600,
)

signature_version='s3v4' を明示的に指定してください。boto3のリファレンスでは、指定しないと後方互換性のため、まだ受け付けているリージョンでは古いSignature Version 2で署名される可能性があり、その場合は Range などの一部のパラメーターやヘッダーが黙って削除されると警告しています。ResponseContentDisposition は任意です。ブラウザーがファイルをインラインで開く代わりに、わかりやすい名前で保存するようになります。

JavaScript(AWS SDK v3)

import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'

const client = new S3Client({ region: 'eu-west-1' })

const url = await getSignedUrl(
  client,
  new GetObjectCommand({ Bucket: 'my-bucket', Key: 'reports/q3.pdf' }),
  { expiresIn: 3600 }
)

expiresIn を省略すると、JavaScriptのpresignerは900秒(15分)を使います。

署名付きアップロードURL(PUT)

同じ考え方は逆方向にも使えます。1つのキーに対する PutObject に署名すると、URLを持っている人はそのオブジェクトを書き込めます。ファイルは相手のデバイスからバケットへ直接送られ、サーバーがデータを扱うことはありません。そのキーのオブジェクトがすでに存在する場合は置き換えられます。

import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'

const client = new S3Client({ region: 'eu-west-1' })

const url = await getSignedUrl(
  client,
  new PutObjectCommand({
    Bucket: 'my-bucket',
    Key: 'incoming/brief.pdf',
    ContentType: 'application/pdf',
  }),
  { expiresIn: 900, signableHeaders: new Set(['content-type']) }
)

await fetch(url, {
  method: 'PUT',
  headers: { 'Content-Type': 'application/pdf' },
  body: file,
})

signableHeaders に content-type を追加すると、URLがそのタイプに紐づけられ、異なる Content-Type でのアップロードは拒否されます。boto3では generate_presigned_url('put_object', Params={'Bucket': ..., 'Key': ..., 'ContentType': 'application/pdf'}) が同等で、クライアントは同じヘッダーを送信する必要があります。

ブラウザーからアップロードする前に確認すべき3つの点:

  • CORS。バケットには、オリジンと PUT メソッドを許可するCORSルールが必要です。マルチパートアップロードでは、ブラウザーが各パートから ETag ヘッダーを読み取る必要があるため、これも公開する必要があります。
  • 固定のキー。訪問者がほかの人のファイルを上書きできないよう、サーバーで一意のキーを生成してください(たとえばUUIDのプレフィックスを付ける)。
  • チェックサム。現在のAWS SDKは、デフォルトでアップロードにCRCチェックサムを追加します。S3互換プロバイダーで署名付きアップロードが署名やチェックサムのエラーで失敗する場合は、クライアントに requestChecksumCalculation: 'WHEN_REQUIRED' を設定してください。

署名付きPOSTは、サイズやコンテンツタイプを制限できるポリシーを備えた、通常のHTMLフォーム向けの別の仕組みです。すべてのS3互換プロバイダーが対応しているわけではない(R2は非対応)ため、署名付きPUTのほうが移植性の高い選択肢です。

大きなファイル: 署名付きマルチパートアップロード

S3への単一の PUT は5 GBが上限で、AWSは約100 MBからマルチパートを推奨しています。マルチパートではファイルを最大10,000個のパート(最後以外はそれぞれ5 MiBから5 GiB)に分割し、パートごとに独立して並列に、再試行しながらアップロードします。署名はパート単位で行います。サーバーがアップロードを開始し、パートごとに UploadPart のURLに署名し、ブラウザーが各チャンクを送信して ETag を返し、サーバーがアップロードを完了します。

import {
  CreateMultipartUploadCommand,
  UploadPartCommand,
  CompleteMultipartUploadCommand,
} from '@aws-sdk/client-s3'

const { UploadId } = await client.send(
  new CreateMultipartUploadCommand({ Bucket: 'my-bucket', Key: 'video/raw.mov' })
)

const partUrl = await getSignedUrl(
  client,
  new UploadPartCommand({ Bucket: 'my-bucket', Key: 'video/raw.mov', UploadId, PartNumber: 1 }),
  { expiresIn: 3600 }
)

await client.send(
  new CompleteMultipartUploadCommand({
    Bucket: 'my-bucket',
    Key: 'video/raw.mov',
    UploadId,
    MultipartUpload: { Parts: [{ PartNumber: 1, ETag: etagFromPartResponse }] },
  })
)

未完了のマルチパートアップロードを数日後に中止するライフサイクルルールを追加してください。そうしないと、放置されたパートが課金対象のストレージを使い続けます。

R2、B2、Spaces、Wasabiでの署名付きURL

SigV4でS3 APIを実装しているプロバイダーは同じコードを受け付けます。変更するのはエンドポイント、リージョン、キーだけです。詳細は異なるため、プロバイダーのドキュメントを確認してください:

  • Cloudflare R2は、有効期限1秒から7日間の署名付き GET、HEAD、PUT、DELETE に対応しています。署名付き POST には対応しておらず、署名付きURLはカスタムドメインではなくS3 APIのドメイン(<ACCOUNT_ID>.r2.cloudflarestorage.com)でのみ機能します。region: 'auto' を使ってください。
  • Backblaze B2は、S3互換APIがAWS CLIやSDKで生成した署名付きURLに対応していると明記しています。
  • DigitalOcean SpacesとWasabiは、それぞれのエンドポイントを指定すれば同じSDKの呼び出しで動作します。

各プロバイダーの料金については、S3、R2、Spaces、Wasabi、B2の比較 をご覧ください。

署名付きURLのよくあるエラーと対処法

エラーよくある原因対処法
SignatureDoesNotMatchメソッド、ヘッダー、クエリが署名したものと異なる、時計のずれ同じContent-Typeとヘッダーを送信し、NTPで時計を同期する
AccessDenied「Request has expired」URLが X-Amz-Date + X-Amz-Expires を過ぎている新しいURLを生成し、署名から使用までの間隔を短くする
指定より早くURLの期限が切れる一時的な認証情報(ロール、EC2、ECS、Lambda)で署名したリンクに必要な期間だけ有効な認証情報で署名する
ExpiredTokenX-Amz-Security-Token の背後にあるSTSセッションが終了した署名する前に認証情報を更新する
新しいURLで403 AccessDenied署名者に s3:GetObject や s3:PutObject がない、またはポリシーが拒否署名するプリンシパルに権限を付与し、バケットポリシーを確認する
AuthorizationQueryParametersError有効期限が604,800秒を超えている、または認証情報のスコープの形式が不正X-Amz-Expires を7日以内にし、バケットのリージョンで署名する
ブラウザーがCORSでブロックするバケットにオリジンとメソッドのCORSルールがないオリジン、GET/PUT、公開する ETag を含むCORSルールを追加する
HeadersNotSigned での AccessDeniedRange は署名されたが If-Range が署名なしで送信された署名済みヘッダーに If-Range を追加する

ヘッダーやクエリ文字列を書き換える企業のプロキシも署名を壊します。あるネットワークでは動作し、別のネットワークでは動作しない場合は、プロキシなしでテストしてください。

セキュリティのベストプラクティス

AWSは署名付きURLをベアラートークンと呼び、保護することを推奨しています。具体的には:

  • 短くする。アップロードや機密ファイルには数分、通常のダウンロードには数時間。再びファイルが必要になったら新しいURLを生成します。
  • 権限の狭いIDで署名する。対象のプレフィックスの読み取り(または書き込み)だけができる専用のIAMユーザーやロールを使えば、リンクが漏れたときの被害を抑えられます。
  • バケットポリシーでガードレールを設ける。s3:signatureAge 条件は一定期間より古い署名を拒否し、aws:SourceIp は使用できるネットワークを制限します。
  • ログに残さない。アプリケーションのログ、アナリティクス、チャット履歴に残った完全な署名付きURLは、期限が切れるまで有効な認証情報です。
  • 足りない機能を想定しておく。パスワード、ダウンロード回数、リンクごとの取り消しはないため、それらが必要な場合はアプリケーションを前段に置いてください。

共有の側面を詳しく、そうした制御を加える選択肢とともに知りたい場合は、バケットを公開せずにS3のファイルを共有する方法 をご覧ください。

Nubboでの署名付きURLの使い方

Nubbo はこの仕組みの上に作られています。エクスプローラーでアップロードすると、サーバーが PUT(100 MB以上の場合は3パートを並列に送るマルチパートアップロード)に署名し、ブラウザーがデータをバケットへ直接送信します。単一ファイルのダウンロードには、1時間有効な署名付き GET URLを使います。アクセスキーはAES-256-GCMで暗号化して保存され、署名のためにサーバー上でのみ使われます。

共有リンク は、素の署名付きURLにない機能を加えます。Nubboは短時間だけ有効なURLを発行する前に、任意のパスワード、有効期限、ダウンロード回数制限を確認し、リンクはいつでも無効にできます。複数ファイルのZIPダウンロードはブラウザー上で作成され、ブラウザーが各ファイルをご自身のバケットから直接ダウンロードします。Nubboがその内容を見ることはありません。正直に言うと例外が1つあります。サムネイルはご自身のバケットの .nubbo-thumbs/ に書き込まれます。Amazon S3、Cloudflare R2、DigitalOcean Spaces、Wasabi、Backblaze B2、公開されたS3互換エンドポイントで同じように動作します。

よくある質問

S3の署名付きURLとは何ですか?

非公開バケット内の1つのオブジェクトへの通常のHTTPSリンクに、クエリ文字列として署名を加えたものです。リンクを持っている人は誰でも、有効期限が切れるまで、署名された1つの操作(通常はダウンロード(GET)またはアップロード(PUT))を実行できます。バケットは非公開のままで、リンクを使う人にAWSの認証情報は必要ありません。

S3の署名付きURLの有効期限の上限はどれくらいですか?

Signature Version 4と長期的なIAMユーザーのキーで署名した場合は7日間(604,800秒)です。引き受けたロールやEC2インスタンスプロファイルなどの一時的な認証情報で署名したURLは、より長い期間を指定しても、その認証情報の期限が切れた時点で使えなくなります。S3コンソールでは1分から12時間の範囲で選べます。

X-Amz-Algorithm、X-Amz-Credential、X-Amz-Date、X-Amz-Expires、X-Amz-SignedHeaders、X-Amz-Signatureはそれぞれ何を意味しますか?

SigV4の認証パラメーターです。Algorithmは署名方式(AWS4-HMAC-SHA256)、CredentialはアクセスキーIDと日付、リージョン、サービスのスコープ、DateはURLが署名された日時、Expiresは有効な秒数、SignedHeadersは署名の対象となるヘッダーの一覧、Signatureは署名そのものです。X-Amz-Security-Tokenは、一時的な認証情報で署名した場合にのみ表示されます。

S3の署名付きURLを有効期限前に取り消せますか?

個別には取り消せません。署名付きURLが期限前に使えなくなるのは、署名した認証情報が取り消し、削除、無効化された場合か、バケットポリシーがリクエストを拒否した場合だけです。個々のリンクを取り消す必要がある場合は、独自のチェックを行ったうえで短時間だけ有効なURLを発行するアプリケーションを前段に置いてください。

S3の署名付きURLは安全ですか?

署名は偽造も改ざんもできず、バケットは非公開のままです。ただし、AWSは署名付きURLをベアラートークンと説明しています。リンクを手に入れた人は誰でも、有効期限まで何度でも使えます。有効期限は短くし、HTTPSを使い、コピーされる可能性のある場所にリンクを掲載しないようにしてください。

署名付きURLはCloudflare R2、Backblaze B2、Wasabiでも使えますか?

Signature Version 4でS3 APIを実装しているプロバイダーであれば一般的に対応しており、プロバイダーのエンドポイントを指定して同じSDKの呼び出しを使います。たとえばCloudflare R2は、有効期限1秒から7日間の署名付きGET、HEAD、PUT、DELETEに対応していますが、POSTフォームによるアップロードとカスタムドメインには対応していません。

署名付きURLがSignatureDoesNotMatchを返すのはなぜですか?

AWSはよくある原因を3つ挙げています: コンピューターの時計のずれ、ヘッダーやクエリ文字列を変更するプロキシ、署名したものとメソッド、ヘッダー、パラメーターが異なるリクエストです。アップロードでよくある原因は、URL生成時と異なるContent-Typeを送信することです。

出典

料金と制限は以下の公式ページに基づいています。プロバイダーは変更することがあるため、契約前にご確認ください。