SJ blog
backend
A

信頼度ランク

S 公式ソース確認済み
A 成功実績多数・失敗例少数
B 賛否両論
C 動作未確認・セキュリティリスク高
Z 個人所感

S3署名付きURL(Presigned URL)— 仕組み・有効期限・セキュリティ注意点

S3署名付きURLの仕組み、有効期限の設定(最大7日間)、IAMユーザーvsロールでの有効期限上限の違い、セキュリティリスクと対策、マルチパートアップロードとの組み合わせを解説。

一言結論

署名付きURLはURLを知っていれば誰でもアクセスできるため有効期限を短く設定することが大原則で、IAMロールで生成した場合は一時クレデンシャルの有効期限が上限になるため最大7日間を期待して生成するとLambda等では意図通りに動作しない。

署名付きURLとは

S3署名付きURL(Presigned URL)は、S3オブジェクトへの一時的なアクセス権を持つURLだ。URLを知っている人なら誰でもアクセスできる(認証不要)。使い捨てのダウンロードリンクや、フロントエンドから直接S3にアップロードする機能に使われる。

通常のS3アクセス: クライアント → AWS認証 → S3
署名付きURL:      クライアント → 署名済みURL → S3(認証不要)

URLにはAWS認証情報(アクセスキーID)+ 有効期限 + 署名が含まれる

生成方法

import boto3
from datetime import timedelta

s3 = boto3.client('s3', region_name='ap-northeast-1')

# ダウンロード用(GET)
download_url = s3.generate_presigned_url(
    ClientMethod='get_object',
    Params={
        'Bucket': 'my-bucket',
        'Key': 'private/document.pdf'
    },
    ExpiresIn=3600  # 1時間(秒単位)
)

# アップロード用(PUT)
upload_url = s3.generate_presigned_url(
    ClientMethod='put_object',
    Params={
        'Bucket': 'my-bucket',
        'Key': 'uploads/user-file.jpg',
        'ContentType': 'image/jpeg'
    },
    ExpiresIn=900  # 15分
)

# 使用例(curlでアップロード)
# curl -X PUT -T file.jpg -H "Content-Type: image/jpeg" "upload_url"

有効期限の上限

有効期限は誰が署名するかによって上限が異なる。

署名主体最大有効期限
IAMユーザー(アクセスキー使用)7日間(604800秒)
IAMロール(STS一時クレデンシャル使用)一時クレデンシャルの残存期間(最大12時間)
EC2インスタンスプロファイルインスタンスの一時クレデンシャルの残存期間

IAMロールで署名した場合、一時クレデンシャルの有効期限が1時間なら署名付きURLも1時間以内に無効化される。Lambda等でPresigned URLを生成する場合はこの制限に注意。

# IAMロール(Lambda)での生成 - 一時クレデンシャルの有効期間が上限
# Lambda実行ロールのクレデンシャルは通常1時間有効

# ❌ これは動作しない(1時間のクレデンシャルで7日のURLは生成できない)
# ExpiresIn=604800  # 7日

# ✅ 実用的な上限
# ExpiresIn=3600  # 1時間以内に設定

Presigned POST(フォームアップロード)

HTMLフォームからのアップロードには generate_presigned_post を使う。アップロードの条件(ファイルサイズ上限、Content-Type等)をサーバー側で制御できる。

# Presigned POSTの生成
response = s3.generate_presigned_post(
    Bucket='my-bucket',
    Key='uploads/${filename}',  # ユーザーのファイル名を使う
    Fields={
        'Content-Type': 'image/jpeg',
        'x-amz-meta-user-id': '12345'
    },
    Conditions=[
        ['content-length-range', 1, 5242880],  # 1B〜5MB
        ['starts-with', '$Content-Type', 'image/'],
        {'x-amz-meta-user-id': '12345'}
    ],
    ExpiresIn=900
)

# HTMLフォームに埋め込む
url = response['url']
fields = response['fields']

セキュリティ上の注意点

1. URLは誰でも使える

署名付きURLはURLを知っている人なら誰でもアクセス可能。
→ HTTPS経由でのみ使用
→ URLをログに記録しない
→ できる限り短い有効期限を設定

2. 生成者の権限が失効すると無効化される

# IAMユーザーのアクセスキーを削除・無効化すると、
# そのキーで生成した署名付きURLはすべて即座に無効化される

# ロールの場合は一時クレデンシャルが失効すると無効化

3. S3バケットポリシーが上書きできる

// バケットポリシーで署名付きURLをブロックすることも可能
{
  "Effect": "Deny",
  "Principal": "*",
  "Action": "s3:GetObject",
  "Resource": "arn:aws:s3:::my-bucket/sensitive/*",
  "Condition": {
    "StringNotEquals": {
      "aws:sourceVpce": "vpce-xxx"
    }
  }
}
// このポリシーがあると署名付きURLからのアクセスも拒否される

CloudFrontの署名付きURLとの違い

特徴S3 Presigned URLCloudFront署名付きURL
バックエンドS3直接CloudFront経由
地理的制限なし可能(Geo Restriction)
IPアドレス制限なし可能(署名付きCookie)
キャッシュなしあり(高速配信)
用途単発のファイルアップロード/ダウンロードコンテンツ配信、動画ストリーミング

まとめ

署名付きURLはサーバーを介さずにS3への安全なアクセスを実現する便利な機能だ。IAMロールで生成する場合の有効期限制限(一時クレデンシャルに依存)と、URLが漏洩した場合の影響範囲(誰でもアクセス可)を理解した上で短い有効期限を設定することが重要だ。