CloudFront 署名付き URL 経由で S3 にアップロードする
CloudFront の署名付き URL を使い、独自ドメイン経由で S3 にアップロードする方法を紹介します。S3 の署名付き URL を直接使えない場合に有用です。
CloudFront と S3 は、どちらも署名付き URL をサポートしています。CloudFront の署名付き URL を使うと、CloudFront ディストリビューションとその独自ドメインを経由してアップロードできます。クライアントの接続先ドメインが制限されている環境で有用です。
Serve private content with signed URLs and signed cookies

信頼された署名者の指定
信頼された署名者として使用するキーグループを作成します。
Specify signers that can create signed URLs and signed cookies
AWSアカウントを信頼された署名者として使用することも可能ですが、AWSはキーグループの使用を推奨しています。詳細はChoose between trusted key groups (recommended) and AWS accountsを参照してください。
この例では、次の要件を満たす RSA キーペアを使用します。2024 年当時の CloudFront キーグループは、ECDSA 256 署名にも対応していました。
- タイプ: SSH-2 RSA キーペア
- フォーマット: Base64 エンコードされた PEM
- キーサイズ: 2048 ビット
次のコマンドでキーペアを作成します。
openssl genrsa -out private_key.pem 2048openssl rsa -pubout -in private_key.pem -out public_key.pem構築
- 公開鍵を
PublicKeyパラメータ(5行目)に渡し、それを使用します(38行目)。 - S3 バケットポリシーで
s3:PutObjectアクションを許可します(27 行目)。 - AllViewerExceptHostHeader オリジンリクエストポリシーを使用します(85 行目)。
AWSTemplateFormatVersion: 2010-09-09Description: Example of CloudFront pre-signed URLs to upload files to S3 Bucket
Parameters: PublicKey: Type: String
Resources: S3Bucket: Type: AWS::S3::Bucket Properties: BucketName: !Sub uploaded-files-${AWS::AccountId}-${AWS::Region}
S3BucketPolicy: Type: AWS::S3::BucketPolicy Properties: Bucket: !Ref S3Bucket PolicyDocument: Version: 2008-10-17 Id: PolicyForCloudFrontPrivateContent Statement: - Sid: AllowCloudFrontServicePrincipal Effect: Allow Principal: Service: cloudfront.amazonaws.com Action: - s3:PutObject Resource: !Sub ${S3Bucket.Arn}/* Condition: StringEquals: "AWS:SourceArn": !Sub arn:aws:cloudfront::${AWS::AccountId}:distribution/${CloudFrontDistribution}
CloudFrontPublicKey: Type: AWS::CloudFront::PublicKey Properties: PublicKeyConfig: Name: signer1 EncodedKey: !Ref PublicKey CallerReference: cloudfront-caller-reference-example
CloudFrontKeyGroup: Type: AWS::CloudFront::KeyGroup Properties: KeyGroupConfig: Name: cloudfront-key-group-1 Items: - !Ref CloudFrontPublicKey
CloudFrontOriginAccessControl: Type: AWS::CloudFront::OriginAccessControl Properties: OriginAccessControlConfig: Name: !Ref S3Bucket OriginAccessControlOriginType: s3 SigningBehavior: always SigningProtocol: sigv4
CloudFrontDistribution: Type: AWS::CloudFront::Distribution Properties: DistributionConfig: Enabled: true HttpVersion: http2and3 Origins: - Id: !GetAtt S3Bucket.DomainName DomainName: !GetAtt S3Bucket.DomainName OriginAccessControlId: !Ref CloudFrontOriginAccessControl S3OriginConfig: OriginAccessIdentity: '' DefaultCacheBehavior: AllowedMethods: - HEAD - DELETE - POST - GET - OPTIONS - PUT - PATCH Compress: true # CachingDisabled # See https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-managed-cache-policies.html#managed-cache-policy-caching-disabled CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad # AllViewerExceptHostHeader # https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-managed-origin-request-policies.html#managed-origin-request-policy-all-viewer-except-host-header OriginRequestPolicyId: b689b0a8-53d0-40ab-baf2-68738e2966ac TargetOriginId: !GetAtt S3Bucket.DomainName TrustedKeyGroups: - !Ref CloudFrontKeyGroup ViewerProtocolPolicy: https-only
Outputs: CloudFrontDistributionDomainName: Value: !GetAtt CloudFrontDistribution.DomainName CloudFrontPublicKeyId: Value: !Ref CloudFrontPublicKey S3BucketName: Value: !Ref S3Bucket次のコマンドで CloudFormation スタックをデプロイします。
PUBLIC_KEY=$(cat public_key.pem)aws cloudformation deploy \ --template-file template.yaml \ --stack-name cloudfront-presigned-urls-example \ --parameter-overrides PublicKey=$PUBLIC_KEYデプロイされたリソースを確認します。
aws cloudformation describe-stacks \ --stack-name cloudfront-presigned-urls-example \| jq ".Stacks[0].Outputs"テスト
次の変数を設定します。
CLOUDFRONT_DOMAIN=<CloudFront domain>KEYPAIR_ID=<Key pair ID>UTC_OFFSET=+9URL を生成します。ここで使う date -v 構文は macOS 用です。
PRESIGNED_URL=$(aws cloudfront sign \ --url https://$CLOUDFRONT_DOMAIN/upload-test.txt \ --key-pair-id $KEYPAIR_ID \ --private-key file://private_key.pem \ --date-less-than $(date -v +5M "+%Y-%m-%dT%H:%M:%S$UTC_OFFSET"))
echo $PRESIGNED_URL# https://<distribution-id>.cloudfront.net/upload-test.txt?Expires=...&Signature=...Key-Pair-Id=...ファイルをアップロードします。
echo 'Hello World' > example.txtcurl -X PUT -d "$(cat example.txt)" $PRESIGNED_URLファイルがアップロードされたことを確認します。
aws s3 cp s3://uploaded-files-<AWS::AccountId>-<AWS::Region>/upload-test.txt ./cat ./upload-test.txtクリーンアップ
次のコマンドで、この例で作成したすべての AWS リソースを削除します。
CloudFrontディストリビューションの無効化には数分かかる場合があります。
aws s3 rm s3://uploaded-files-<AWS::AccountId>-<AWS::Region>/upload-test.txtaws cloudformation delete-stack --stack-name cloudfront-presigned-urls-exampleまとめ
信頼されたキーグループで CloudFront の URL に署名し、その URL へファイルを PUT することで、S3 の署名付き URL を直接使わず、独自ドメイン経由で S3 にアップロードできることを確認しました。
URL の署名に使う秘密鍵は AWS へアップロードしません。CloudFront が CloudFrontPublicKey 経由で受け取るのは公開鍵だけで、秘密鍵は aws cloudfront sign を実行する環境に残ります。信頼されたキーグループを使えば、鍵が漏洩した場合も IAM 認証情報を変更せずに鍵を交換できます。
アップロードを成功させるには、キャッシュビヘイビアで PUT を許可して署名用キーグループを信頼し、オリジンアクセスコントロールと S3 バケットポリシーで CloudFront に s3:PutObject を許可する必要があります。URL を生成できただけで設定が正しいとは判断せず、署名、PUT、S3 からの読み戻しまでを通してテストしてください。
Related posts
Amazon S3 イベント通知の s3:TestEvent を処理する
Amazon S3 はイベント通知の設定時に s3:TestEvent メッセージを送信します。通常のイベント通知とは構造が異なるため、コンシューマー側で個別に処理する必要があります。
Cognito User Pools と OIDC で Slack サインインを実装する
Cognito user pool を OIDC 経由で Slack と連携させ、"Sign in with Slack" を Amplify で Next.js アプリケーションに組み込みます。
Lambda Web Adapter で FastAPI を AWS Lambda にデプロイする
FastAPI で書いた API バックエンドをコンテナ化し、Lambda Web Adapter と AWS CDK を使って単一の Lambda 関数へデプロイします。
API Gateway WebSocket:モック統合の実装
バックエンドの Lambda を使わず、モック統合のみで API Gateway WebSocket API を構築し、あらかじめ用意したレスポンスを返します。
AWS EventBridge Scheduler:スケジュールに沿って EC2 を起動・停止する
Lambda を介さず、EventBridge Scheduler から EC2 API を直接呼び出し、cron スケジュールに従って EC2 インスタンスを起動・停止します。
