俺の報告

RoomClipを運営するエンジニアの日報(多分)です。

日報 #106 - S3でリクエストの署名と認証

乾燥しておるようです。
毎年この時期というのは、寒いし乾燥してるしインフルエンザも流行るしで、
病気リスクの高い時期なわけです。
その割に大事なイベントが多い時期でもあるわけです。
受験とか特に。
ちなみに僕は浪人時のセンター試験当日にインフルエンザになりました組です。
浪人・センター・インフルのSEO、狙っていきたいです。

さて、
本日は「これ豆な」的な話。
S3のファイルに期限付き認証を付ける話。

BASIC認証とかPHPとかが動かないS3ですので、
意外とアクセス制限をかけたファイル共有は面倒だったりします。
ですが、不可能というわけではありません。
S3へのリクエストに、「署名による認証」を付けることは可能です。

ドキュメントはこちらです。
http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/RESTAuthentication.html
まぁぶっちゃけこれをよくよく読んでね☆で終わりな話なのですが、
実際url生成のphpサンプルとかがあったほうがより便利だと思いますので、
今回僕がやった方法を紹介いたします。

目標はこんな感じ。
「S3にあるファイルを、一定期間だけ公開する認証付きURLを生成する。」
なんか複雑な言い回しにしているのは、
いわゆるファイル添付サービスみたいな「Webページにアクセスしてパスワードを入力してファイルゲット」というシステムではない、 という理由からです。
早い話がパスワードがついたURLを特定の相手に送信して、クリックさせ、DLさせる。
そのURLは時期がくれば自然と利用不可能になるし、そのURLを直接教えてもらう以外にアクセス方法は事実上存在しない、
みたいな共有方法です。
僕としてはメールでパスワードを送るのと大して変わりはないと思っていますので、
簡単なファイルならこれで共有してしまいます。

前置きが長くなったのでサクッとURLの生成方法を。

どういうシステムでS3のファイルに認証アクセスをするのかと申しますと、
まぁいわゆる「署名による認証」という感じです。
「目的のファイルへのリクエスト文」+「有効期限の情報」みたいなテキスト文を、
秘密の情報を用いてハッシュ化し、
実際のリクエスト文に署名として付与しながらアクセスする。
AWS側でもらったリクエスト文を使って改めて署名を作成してみて、
付与された署名と一致すれば認証成功、晴れてDLが始まる、といったシステムです。

なので問題はその「署名」の作り方です。
冒頭のドキュメントにぜーーんぶ署名の作り方がかいてありますが、
それを見ろってのも面倒な人向けにPHP組みました。
さっさと署名を作りたい方はお使いください。

<?php
// AWS IAM INFO
// IAM = hoge_iam
$accessKey = "HOGEHOGEACCESSKEY";
$secretAccessKey = "HOGEHOGESecretAccessKey";

// S3 INFO
$S3Host = "s3.amazonaws.com";
$bucketName = "EXT-DL";
$URLFormat = $S3Host."/".$bucketName."/%s?AWSAccessKeyId=%s&Expires=%s&Signature=%s";

// FILE NAME
if (!isset($argv[1])) {
     echo "Need filename.\n";
     exit;
}
$fileName = $argv[1];

// HTTP-Verb
$httpVerb = 'GET' . "\n";
 
// Content-MD5
$contentMd5 = "\n";
 
// Content-Type
$contentType = "\n";
 
// EXPIRE
$datetime = new DateTime('+ 3days', new DateTimeZone('UTC'));
$date = $datetime->format(DateTime::RFC1123);
$expires = $datetime->format('U');

// CanonicalizedAmzHeaders
$canonicalizedAmzHeaders = '';
 
// CanonicalizedResource
$canonicalizedResource = "/".$bucketName."/".$fileName;
 
// StringToSign
$stringToSign = $httpVerb . $contentMd5 . $contentType . $expires . "\n"
              . $canonicalizedAmzHeaders . $canonicalizedResource;

// Signature
$hash = hash_hmac('sha1', $stringToSign, $secretAccessKey, true);
$signature = urlencode(base64_encode($hash));

echo "\n===stringToSign===\n";
echo $stringToSign;
echo "\n==================\n";
echo "\n=======URL=======\n";
echo sprintf($URLFormat, $fileName, $accessKey, $expires, $signature);
echo "\n==================\n";
echo "\n=======EXPIRE=======\n";
echo $date;
echo "\n==================\n";
?>

と、こういう感じです。
これをsignature.phpとかに保存して、引数にファイル名を付与すればよいのです。
(EXPIREを変更したければ$datetimeのところを+ 7daysとかにしてちょ)

php signature.php hoge.txt

また、AWSのアクセスキーやシークレットアクセスキーが必要になるので、
IAMでDL専用のユーザを作成しておくとよいと思います。
それから、S3のDL対象ファイルですが、permissionのところで、Authenticated Usersにopen/Download権限を与えておきましょう。
さすればサクッとDLできるであろう…

今日は寒いし、乾燥ているから、以上でした。