概要
S3には、ウェブサイトホスティングを有効にしないまま、期限付きのURLを発行して外部からアクセスできるようにする機能があります。今回は、Lambda(Node.js)からS3の期限付きURLを取得し、CURLで期限付きURLにアクセスしてみます。
用途
Lambdaから期限付きURLを取得する処理は、サーバレス アーキテクチャでよく登場します。
■ サーバーレス アーキテクチャの認証の流れ
- クライアントはAPI Gatewayにアクセスします。API Gatewayは外部からのアクセスに対して認証を行い、S3のファイルに対するアクセス権のチェックを行います。
- 認証に通ったことを確認したうえで内部ではLambdaがS3の期限つきURLを取得して応答します。
- クライアントはAPI Gatewayから応答されたS3の期限つきURLにアクセスします。
今回の「S3の期限付きURLをLambdaから取得する」は②の部分の処理になります。
GET Object
S3
バケットを作成し、ファイルをアップロードしておきます。
静的ウェブサイトホスティングは、外部から直接アクセスできないように
◎ ウェブサイトのホスティングを有効にしない
にセットします。
Lambda
■ IAMポリシー
S3バケット内のファイルに対するGET権限を与えます。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject"
],
"Resource": "arn:aws:s3:::<バケット>/*"
}
]
}
■ コード
exports.handler = (event, context, callback) => {
let AWS = require('aws-sdk');
let s3 = new AWS.S3();
let params = {
Bucket: '<バケット>',
Key: '<オブジェクトキー>',
Expires: <秒数>
};
s3.getSignedUrl('getObject', params, function (err, url) {
callback(null, url);
});
};
■ Lambdaのテスト
Lambdaのテストボタンを押してみます。
Execution result: succeeded に期限付きURLが表示されます。
CURLで期限付きURLにアクセス
■ 正常なアクセス
Lambdaのテストで表示された期限付きURLにCURLでアクセスしてみます。
クエリパラメータが複数あるのでURLを「""」で囲む必要があります。
$ curl -o <ファイル名> -X GET "<期限付きURL>"
期限内にURLにアクセスすると、<ファイル名>をダウンロードすることができました。
■ 期限切れ
期限が過ぎてからアクセスしてみます。
期限(<Expires>)<サーバー時刻(<ServerTime>)
のエラーが返ってきてファイルが取得できなくなります。
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>AccessDenied</Code>
<Message>Request has expired</Message>
<Expires>2017-02-04T12:55:14Z</Expires>
<ServerTime>2017-02-04T12:55:36Z</ServerTime>
<RequestId>137B27F263A55608</RequestId>
<HostId>ZrQWyt7Rf0sPwQPfUbYUy34wSFdWZxSBC3JFU8YGU6w0NT+18ibIdAHE5suoC6MoZttTwAI9dyQ=</HostId>
</Error>
■ URLを改竄
URLを改竄してみます。<エポック秒>を改竄してアクセスしてみます。
不正が検知されエラーが返ってきます。
<Error>
<Code>InvalidToken</Code>
<Message>The provided token is malformed or otherwise invalid.</Message>
<Token-0>FQoDYXdzEOrFFFFFFFFFFwEaDDORFHT0g0nL4wg7oSLlAUy2ILzR33Szq98fHk8fOEKj89RBlkVLAsZITfBaKBndNj8MMS0HtKilZ3utMx8krPwFNBSBBBGwzBSAvBUutX4hRQR3qW0jvS4uL6VtTFPmxNyzSQBvdvHhWIZFnXeTp1cd1xxDunPVEaRiJNqwvegcmvVIiVfW1DLCIA1FNHSAp8FF9kKVvjTbtB5nv2BrnlTOcfphLtTyhAxMs1aeMATRc2cJ6fs068fxfJFIifTpfLLYcCQ4kbGhFBmjYB7Kr4U10rmdGY4rZeOenhc9YPeZShgv0QP5IabFBzFB6cFEFa5EQD8o5LLWxAUD</Token-0>
<RequestId>0A2B6F8EC80D0A8F</RequestId>
<HostId>P6VveqnTd9twOSxV94D7vtRvwmiS/k9LYqJh+9iLKtNJcLdNAeHtCrVEUws+Ftfb2bS9gQOItY8=</HostId>
</Error>
■ セキュリティトークンを改竄
セキュリティトークン(x-amz-security-token)を改竄してアクセスしてみます。
シグネチャが一致しないエラーになります。
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>SignatureDoesNotMatch</Code>
<Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>
<AWSAccessKeyId>XXXXXXXX</AWSAccessKeyId>
<StringToSign>
GET
1486269940
x-amz-security-token:YYYYYYYY
</StringToSign>
<SignatureProvided>rTfGRqWMWAKLixOm/92tqt6WVlA=</SignatureProvided>
<StringToSignBytes>00 01 02 03 ...</StringToSignBytes><RequestId>A5962F972E4E3D14</RequestId>
<HostId>do4mKLzPZZpVmH7eCDhF6cmsPaY53oPF5LYsoLuu65qKUaF2QcTpJXbpXVOEx42xQ/TflrL6cgw=</HostId>
</Error>
■ ファイルが存在しない
期限付きURL発効後にファイルが削除された場合は、「AccessDenied」になります。
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>AccessDenied</Code>
<Message>Access Denied</Message>
<RequestId>9F6816DF55ACC3B4</RequestId>
<HostId>vEh9mzdRPUHi8D3UAyzOn/lc3aV/Bz5aRZNRnbYTU8Vdrc+92Dpy65Kpb+yrnzw9fGHTbtpfsRQ=</HostId>
</Error>
PUT Object
S3
バケットを作成しておきます。
静的ウェブサイトホスティングは、外部から直接アクセスできないように
◎ ウェブサイトのホスティングを有効にしない
にセットします。
Lambda
■ IAMポリシー
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject"
],
"Resource": "arn:aws:s3:::<バケット>/*"
}
]
}
■ コード
exports.handler = function (event, context, callback) {
"use strict";
const AWS = require('aws-sdk');
let s3 = new AWS.S3();
const BUCKET = '<バケット>';
const KEY = '<オブジェクトキー>';
const CONTENT_TYPE = 'image/jpeg'; // image/png, image/tiff, etc...
var params = {
Bucket: BUCKET,
Key: KEY,
Body: '',
ContentType: CONTENT_TYPE,
Expires: 1800
};
s3.getSignedUrl('putObject', params, function (err, url) {
if (err) {
callback(err);
return;
}
callback(null, url);
});
};
■ Lambdaのテスト
Lambdaのテストボタンを押してみます。
Execution result: succeeded に期限付きURLが表示されます。
CURLで期限付きURLにアクセス
■ 正常なアクセス
ローカルの /path/to/file.jpg
にファイルを用意し、curlを実行します。
curl -H "Content-Type: image/jpeg" --data-binary "@/path/to/file.jpg" -X PUT "<期限付きURL>"
期限内にアクセスすると、ファイルをS3にアップロードすることができました。
以上です。