cloudfront+S3を用いた画像投稿機能においてよりセキュアに通信するべく署名付きURLを発行して画像を投稿するというやり方を挑戦してみました。
cloudfrontの設定画面が変わったということもあり、他の記事だと画面が違かったりしたので誰かの役に立てば幸いです!
今回、投稿はawsSDKのgetSignedUrl関数を使用し、取得にはcloudfrontを経由したキーペアによる署名作成を行いました。
今回AWS構築以外の部分については記事に書かれていないのでご了承ください。
コンピューティング
今回自分はsupabaseのコンピューティングであるEdge Functionを使用しました。また、supabase cliを使用してローカルで開発を行いました。
supabase cliはかなり使いやすく、コンテナも supabase startとするのみで立ち上がったので使いやすかったです。
キーの作成
まず、署名を作成するためのキーをローカルのパソコンで作成します。以下のコマンドを用いて作成しました。
# 秘密鍵の作成
openssl genrsa -out private_key.pem 2048
# 秘密鍵を用いて公開鍵の作成
openssl rsa -in private_key.pem -pubout -out public_key.pem
コマンドを打ったディレクトリにprivate_key.pemとpublic_key.pemが作られるはずなので控えておきます。
S3バケットの作成
次にs3バケットを作成します。「バケットを作成」からバケット名などを入力していきます。
その時以下のパブリックアクセスをすべてブロックするにするようにしてください!

あとはそのままデフォルトで作成をします。
IAMユーザーの作成
s3への投稿権限の持つユーザーを作成します。今回はs3-uploaderというユーザーを作ろうと思います。
IAMへいき、ユーザータブからユーザー作成をクリック、名前を入力して次へを押すと以下のような画面になると思います。ここで、「ポリシーを直接アタッチ」をおし、「ポリシーの作成」を押します。そうすると別ブラウザでポリシー作成画面が開くと思います。

ポリシー作成画面のjson編集画面で以下を入力します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::YOUR_BUCKET_NAME/*"
}
]
}
バケット名は自分の作成したバケット名に変更してください。
そしたらポリシー名をつけて、作成を押してください。
作成が完了したらIAMユーザー作成画面に戻ります。
ユーザー画面のポリシー検索欄に先ほど作ったポリシー名を入力して、選択します。
そしたら確認画面へいき、あとは作成をクリックします。
これでIAMユーザーが作成されました。作成後、アクセス認証情報というタブから「アクセスキー」を作成し、キーとシークレットキーどちらも必ず控えておいてください。

CloudFrontの作成
次にCloudFront側の設定をします。
キーの登録
まず、左のハンバーガーメニューを押して、「パブリックキー」を押します。そうすると画像のような画面になると思うので、ここに先ほど作った公開鍵(public_key.pem) を登録します。

ここで、作成完了した後に表示されるパブリックキーIDは後で使うので控えておきます。
作成が完了したら次にパブリックキーの下にある「キーグループ」を押します。
キーグループを作成を押し、名前を入力した後、画像赤枠の部分をクリックすると先ほど作成したキーが出てくると思うので、それを選択します!

ディストリビューションの作成
キーの作成が終わったら次にディストリビューションの作成を行います。
作成画面に行くと以下のような画面になると思います。

ここでは、名前のみを登録して次に進みます。
次にオリジン設定画面に進むと思います。

ここでは「Amazon S3」を選択し、「S3 origin」で先ほど作成したs3バケットを選択します。
オリジンパス部分はもしフォルダなどを中に指定している場合はそのパスを書いてあげてください。
その下にsettingsという欄があるのですが、これは後からでも設定できるので一旦デフォルトのまま次に進みます。

次にWAFを設定するかどうか聞かれます。これは個人の用途に則ってください。自分は今回つけませんでした!

そうすると最後確認画面が出るので「create distribution」を押します。

作成が完了になったら、ディストリビューションドメイン名だけ控えておきます。(画像取得時に使います)
OACの設定をする
作成後、ステータスがActiveになったらそのディストリビューションをクリックします。

この画面が出たら、オリジンを選択して、「編集」をおします。
編集画面に行くと、真ん中の部分にオリジンアクセスという欄があるので、そこを「Origin access control settings (recommended)」を選択します。
そしてOAC(Origin access control)に作成したs3バケットを指定します。

⚠️すでに設定されてるかもしれませんが、その下に画像のような部分があると思うので、念の為、ポリシーをコピーしてs3に反映されてるかどうか確認するといいと思います。

そして変更を保存します
ビヘイビアの編集
次にビヘイビアのタブに行きます。同様にビヘイビアが作成されてるかと思うので、選択し、編集を押します。
そうすると以下のような画面が開くのでここの特にビューワーのアクセスを制限するというところに注目します

ここを「Yes」とし、信頼された認可タイプは「Trusted key groups」にし、キーグループを追加というところに、先ほど作ったキーグループを選択します。
これでAWSの設定は完了です!!
沼ったところ
キーペアがうまく認識されなかった
これは取得時の話ですが、取得の際はcloudfrontを経由するので、cloudfrontに設定した公開鍵と呼び出し元にある秘密鍵を使って署名を行います。
この時、秘密鍵の持ち方で自分はそのまんま.envにぶち込んでしまっていたのですが、1行にしなきゃだそうで、ここにやられてました。明示的に\nとする必要があるようです
Before
-----BEGIN RSA PRIVATE KEY-----
MIIC...
...XYZ
-----END RSA PRIVATE KEY-----
After
"-----BEGIN RSA PRIVATE KEY-----\nMIIC...\n...XYZ\n-----END RSA PRIVATE KEY-----"
その後、コード内で以下のように改行文字を本当の改行に戻すといいようです。
// TypeScriptでの例
const privateKey = Deno.env.get("CLOUDFRONT_PRIVATE_KEY")!.replace(/\\n/g, '\n');
まとめ
今回は署名付きURLを発行するためのAWSの構築についてまとめました。初めての試みだったので詰まることが多かったですが、学びが多かったです。
時間があったらsupabase cliでの実装部分についても別記事で出したいと思います!