こんな時どうする?
ケース1
自分が管理するS3バケットに対して、他の人にファイルをアップロードしてもらいたい
- 相手からファイルをもらって自分でアップロードする?
- その人のために新しくアクセスキーとシークレットキーを発行する?
ケース2
SPAなどでフロントエンド側からS3バケットへファイルを直接アップロードしたい
- フロントエンドからバックエンドへ一旦ファイルをアップロードして、サーバーで受け取ったファイルをS3へアップロードする?
サーバーサイドのバリデーションが必要な場合は有効な方法ですが、サーバーに負荷がかかってしまいます。
どちらのケースでも他の人やフロントエンド側にシークレットキーを渡さずにアップロードしたいものです。
注意
この記事で紹介している方法は、署名バージョン2です。
2014年1月30日以降の新しいリージョンでは署名バージョン4のみがサポートされるため、それらのリージョンへのリクエストは署名バージョン4で行う必要があります。
Authenticating Requests (AWS Signature Version 4)
やり方
S3の「署名付きURL」という仕組みを使います。
ファイルアップロード用の「署名付きURL」を作るにはプログラムが必要です。
(表示用の署名付きURLであればAWSコンソールから取得することができます)
流れ
- アクセスキーとシークレットキーを使って署名付きURLを生成します
- 相手側に生成した署名付きURLを送ります
- 署名付きURLに向けてファイルをアップロードします
署名付きURLを作る時にはシークレットキーが必要ですが、作ったURLを使う時はシークレットキーは必要ありません。
(SPAのケースだと「署名付きURL」の生成はサーバーサイドで行う必要があります)
準備
- AWS CLI のインストールと設定が済んでいること
- node のインストールと設定が済んでいること
- S3バケットを作成する
- S3バケットにアクセスできるユーザーを作る
- ユーザーのアクセスキー・シークレットキーを作る
- AWS CLIにユーザーのアクセスキー・シークレットキーを設定する
この辺りの詳細は割愛します。
署名付きURLの生成
パッケージインストール
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner
サンプルプログラム
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');
const { getSignedUrl } = require('@aws-sdk/s3-request-presigner');
// 署名付きURLを生成する関数
const generatePresignedUrl = async (region, bucketName, objectKey, expires) => {
const client = new S3Client({ region: region });
const params = { Bucket: bucketName, Key: objectKey };
try {
const url = await getSignedUrl(client, new PutObjectCommand(params), { expiresIn: expires });
console.log("署名付きURL:", url);
} catch (error) {
console.error("エラーが発生しました:", error);
}
};
// コマンドライン引数の処理
const parseArgs = () => {
const args = process.argv.slice(2);
if (args.length < 4) {
console.error("使用法: node generatePresignedUrl.js <リージョン> <バケット名> <オブジェクトキー> <署名付きURLの有効期限>");
process.exit(1);
}
return {
region: args[0],
bucketName: args[1],
objectKey: args[2],
expires: args[3]
};
};
// メイン関数
(async () => {
const { region, bucketName, objectKey, expires } = parseArgs();
await generatePresignedUrl(region, bucketName, objectKey, expires);
})();
サンプルプログラム実行して「署名付きURL」生成
node generatePresignedUrl.js リージョン バケット名 ファイル名 有効期限秒数
ここから相手側で行う処理です
署名付きURLを相手側に伝えます
(あるいはサーバーサイドからフロントエンドへURLを送ります)
ファイルをアップロードする
次の例はcurlコマンドを使っていますが他のツールでも構いません。プログラムでアップロードすることも難しくないでしょう。
curl -X PUT "署名付きURLを貼り付ける" \
-H "Content-Type: image/jpeg" \
-H "x-amz-content-sha256: UNSIGNED-PAYLOAD" \
--data-binary "@ファイル名"
注意
-
「Content-Type: image/jpeg」はアップロードするファイルに応じて変更してください
-
ファイル名の前にある@は必要です。curlコマンドはファイル名の前に@を付けると「Content-Length」を自動で算出してヘッダーに差し込んでくれます
シークレットキーを渡さずにファイルのアップロードができました
署名バージョン4
今回は「署名バージョン2」についてのサンプルでしたが、今機会があれば「署名バージョン4」についてもお伝えできればと思います。