角待ちは対空

おもむろガウェイン

PerlでAWS S3の事前署名付きURLを生成する

忙しくて昨日一昨日サボったけどどっかでつじつま合わせるよ。

docs.aws.amazon.com

基本的にこの通り実装すれば良い。SDKがない言語だと苦労する。

use strict;
use warnings;
use utf8;

use Digest::SHA qw(hmac_sha256_hex hmac_sha256 sha256_hex);
use URI;
use URI::QueryParam;
use DateTime;
use feature qw(say);

my $ACCESS_KEY = '';
my $SECRET_KEY = '';
my $HOST = 'img.yux3.net.s3-ap-northeast-1.amazonaws.com';
my $REGION = 'ap-northeast-1';

my $path = '/うたわれるもの.gif';

my $dt = DateTime->new({
    year   => 2017,
    month  => 12,
    day    => 13,
    hour   => 21,
    minute => 17,
    second => 0,
    time_zone => 'Asia/Tokyo',
});
$dt->set_time_zone('UTC');
my $yyyymmdd = $dt->strftime('%Y%m%d');
my $iso8601 = $dt->strftime('%Y%m%dT%H%M%SZ');
my $expire = 1000;


my $u = URI->new();
$u->path($path);

my $scope = "$yyyymmdd/$REGION/s3/aws4_request";

$u->query_param_append("X-Amz-Algorithm", "AWS4-HMAC-SHA256");
$u->query_param_append("X-Amz-Credential", "$ACCESS_KEY/$scope");
$u->query_param_append("X-Amz-Date", $iso8601);
$u->query_param_append("X-Amz-Expires", $expire);
$u->query_param_append("X-Amz-SignedHeaders", "host");

my $canonical_request = << "DATA";
GET
@{[$u->path]}
@{[$u->query]}
host:$HOST

host
UNSIGNED-PAYLOAD
DATA
chomp $canonical_request;

my $string_to_sign = << "DATA";
AWS4-HMAC-SHA256
$iso8601
$scope
@{[sha256_hex($canonical_request)]}
DATA
chomp $string_to_sign;


my $signature = hmac_sha256_hex($string_to_sign, hmac_sha256("aws4_request", hmac_sha256("s3", hmac_sha256($REGION, hmac_sha256($yyyymmdd, "AWS4" . $SECRET_KEY)))));

$u->scheme("http");
$u->host($HOST);
$u->query_param_append("X-Amz-Signature", $signature);

say $u->as_string;

生成されるURLはこんな感じ。 http://img.yux3.net.s3-ap-northeast-1.amazonaws.com/%E3%81%86%E3%81%9F%E3%82%8F%E3%82%8C%E3%82%8B%E3%82%82%E3%81%AE.gif?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAI7WRPIABGYYL47EA%2F20171213%2Fap-northeast-1%2Fs3%2Faws4_request&X-Amz-Date=20171213T121700Z&X-Amz-Expires=1000&X-Amz-SignedHeaders=host&X-Amz-Signature=4fd13192750125a94dd0cb4dd87b4897e4e7ba24936c2ec344423a0d781fc9ae (1000秒でexpireされるのでそのうち見えなくなる)

もちろん署名がないと見えない。 http://img.yux3.net.s3-ap-northeast-1.amazonaws.com/%E3%81%86%E3%81%9F%E3%82%8F%E3%82%8C%E3%82%8B%E3%82%82%E3%81%AE.gif

blog.yux3.net