まず結論
LambdaのテストのOUTPUTにはエスケープのバックスラッシュが混入させてしまう場合があるので注意!
OUTPUT出力のURLをAlt+Clickすると混入するので特に注意!
何をして躓いたか
ApiGateway経由のLambdaで署名付きURLの発行を行い、これを使ってS3へのアクセスを行うというよくある奴です。

これのためLambdaでおよそ以下のようなコードを作成し、テスト機能でデバッグを行いました。
// クレームとクエリからorganizationとfilenameを取得
const claims = event.requestContext.authorizer.claims;
const organization = claims['custom:organization'];
const filename = event.queryStringParameters.filename;
const key = `${organization}/${filename}`;
// S3クライアントを初期化
const s3Client = new S3Client({
region: REGION,
signatureVersion: 'v4'
});
// 署名付きURL生成用のコマンド
const command = new GetObjectCommand({
Bucket: BUCKET_NAME,
Key: key,
contentType: CONTENT_TYPE
});
// 署名付きURLを生成
const url = await getSignedUrl(s3Client, command, {
expiresIn: EXPIRES_IN
});
return {
statusCode: 200,
body: JSON.stringify({ url })
};
LambdaにはS3へのReadonlyロールをつけ、動作する状態にしてからのテストです。
署名付きURlはOUTPUT内に以下のように正常に発行されました。
"{\"url\":\"https://s3.../test.bucket/test_organization/test.jpg(中略)&x-id=GetObject\"}"
しかしAlt+ClickでこのURLに飛ぶと以下のようなエラーが発生していました。
<Error>
<Code>SignatureDoesNotMatch</Code>
<Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>
~省略~
右葉曲折の始まり
約半日様々な記事を当たり方法を試しました。s3ClientのsigunetureVersionの指定、GETではcontentTypeを指定するとSigunatureErrorとなるなど。CLIで発行した署名付きURLとの比較を行って文字とにらめっこしてみたり。古い記事を眺めて「ACLをAll UserにReadonlyで開放?」ACLは非推奨だし、署名の一時認証は自動で行われるから関係ないでしょと唸ってみたり。
最終的にテストを諦めAPI Gatewayからなら動くのではないかと試し、出力されたURLをコピペしたところ。正常にアクセス可能ではありませんか。
そこでようやく気が付きました。
テスト出力のURLの末尾にバックスラッシュがついている
署名付きURLは発行されたものとの完全一致が必要不可欠なのでこれではアクセスできないのも当然です。
何故バックスラッシュが混入したのかと、もう一度エラーを起こした作業を再現してみると…
Oh…
OUTPUTに出力されたURLからAlt+Clickで移動しようとした際のポップアップを見ると、エスケープのバックスラッシュもURLに含まれてしまっています。恐らくダブルクオートから次のダブルクオートまでをURLと認識するため、後ろのダブルクオートのエスケープ文字が混じってしまっているのでしょう。
私がURLを確認した際もクエリ部分がかなり長かったため、末尾の異物をトレイリングスラッシュに見間違えたのだと思います。
教訓
- AWSのLambdaのテストのOUTPUTでURLのAlt + Clickは気をつけよう
- Error文に変な形のエラー
x-id=GetObject%5Cがあったのに気付けなかった - URLは長くても注意して見ること
ご笑納いただけたら幸いです。
