CloudFrontを経由して、S3バケットへアップロードしたコンテンツへのアクセスについて解説したいと思います。
構成図は以下です。

目的: CloudFront経由でアクセスすることを目的としているため、S3のオリジン内に直接アクセスされては無意味になってしまうため、以下の方法でオリジンへのアクセスを制御する。
S3バケットへ付与するポリシーは後ほど紹介しますが、前提として上記が必要であることを覚えておきましょう。
Table of Contents
CloudFrontとは
Amazon CloudFrontとは、動画、アプリケーション、静的、動的なコンテンツを迅速かつ安全に配信できるCDN (Content Delivery Network)サービスです。
エッジロケーションというデータセンターの世界的なネットワークを経由してコンテンツを配信します。
閲覧者がウェブサイトからファイルをリクエストすると、CloudFront は自動的に最も近いエッジロケーションのファイルのコピーにリクエストをリダイレクトします。
これにより、遠い場所にあるデータセンターからコンテンツをリクエストした場合よりもダウンロード時間が高速になります。
今回はオリジン(コンテンツが格納されているサーバ)をS3として、静的なフファイルを配置し、CloudFront経由でS3へアクセスする手順やコンテンツをS3へアップロードした際の暗号化について深堀しました。
S3 handson
早速ですが、handsonをしながら途中途中解説していき、ポイントで深堀りしていきたいと思います。
S3に対してオリジンアクセスコントロールのアクセス許可をしてCloudFront経由でコンテンツにアクセスする方法の手順を解説します。
S3バケットの作成
バケット名を入力します。

ACL 無効を選択します。

このバケットのブロックパブリックアクセス設定のパブリックアクセスをすべてブロック
のチェックを外します。

現在の設定により、このバケットとバケット内のオブジェクトが公開される可能性があることを承認します。
にチェックを付けます。

デフォルトの暗号化は以下の通り選択します。
暗号化タイプ: Amazon S3 マネージドキーを使用したサーバ―側の暗号化 (SSE-S3)
バケットキー: 有効にする

これでバケットを作成します。
全世界へ静的Webサイトの公開
作成したS3バケットをクリックします。

プロパティタブを選択します。

静的ウェブサイトホスティングの項目までスクロールし、編集ボタンをクリックします。

静的ウェブサイトホスティングを”有効にする” に変更し、
インデックスドキュメント: index.html
エラードキュメント: error.htmlと入力し、変更の保存をします。

オブジェクトをアップロードします。
作成したS3バケット名をクリックし、以下の画面からオブジェクトをアップロードします。

アップロードするファイルはなんでも構いませんが、今回は以下のindex.html
とstyle.css
をアップロードします。
<!DOCTYPE html>
<html lang="en">
<head>
<title>Hello world!</title>
<meta charset="utf-8">
<link rel="stylesheet" href="./css/style.css" />
</head>
<body>
<h1>Hello world!</h1>
</body>
</html>
@font-face {
font-family: AmazonEmber;
src: url("https://a0.awsstatic.com/libra-css/fonts/amazon-ember/AmazonEmber_Lt.woff");
font-weight: 400;
font-style: normal;
font-size: 12pt;
}
body {
background: #236192;
font-family: AmazonEmber, Helvetica Neue, Helvetica, Arial, sans-serif;
color: #ffffff;
max-width: 75em;
margin: 0 auto;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
}
上記と同様のファイルはAWS公式からダウンロードできますので、併せてリンクを載せておきます。
S3バケットポリシーの編集
バケットポリシーを付与することで、S3バケットのオブジェクトにアクセスすることができます。
バケット名をクリックします。

アクセス許可を選択します。

バケットポリシーの編集からバケットポリシーを編集します。

以下のバケットポリシーを付与します。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::dev-s3-content/*"
}
]
}
AWS公式のActionsリファレンスにどんなことができるのかの内容が記載されているのでこちらを参照すると良いでしょう。
内容はdev-s3-content/*
以下全てのファイルに対してS3からオブジェクトを取得するためのアクセス許可を付与しています。
インデックスドキュメントの設定
上記のバケットポリシーを設定することで、S3へアップロードしたindex.htmlを全世界に公開することができましたが、インデックスドキュメントについて少し触れていきたいと思います。
インデックスドキュメントとはAWS公式ドキュメントでは以下のように説明されています。
バケットに対して静的ウェブサイトホスティングを有効にする場合は、インデックスドキュメントの名前 (index.html など) を入力します。バケットに対して静的ウェブサイトホスティングを有効にした後、インデックスドキュメント名を含む HTML ファイルをバケットにアップロードします。 ルートレベル URL の末尾のスラッシュは省略可能です。たとえば、index.html をウェブサイトのインデックスドキュメントとして設定した場合は、以下の URL のどちらからも index.html が返されます。
https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/IndexDocumentSupport.html
http://example-bucket.s3-website.Region.amazonaws.com/
http://example-bucket.s3-website.Region.amazonaws.com
フォルダごとアップロードした場合、例えば以下のようなディレクトリ構成でアップロードした場合
hello-world-html/
├ index.html
├ css/
└style.css
以下のURLではindex.htmlは表示できませんでした。
http://example-bucket.s3-website.Region.amazonaws.com/
http://example-bucket.s3-website.Region.amazonaws.com
結論を先に言うと以下のようにディレクトリを指定することで、インデックスドキュメントを探しに行けるようです。
http://example-bucket.s3-website.Region.amazonaws.com/hello-world-html
http://example-bucket.s3-website.Region.amazonaws.com/hello-world-html/index.html
フォルダ構造をバケット内に作成する場合は、各レベルにインデックスドキュメントが存在している必要があります。各フォルダでは、インデックスドキュメントは同じ名前 (index.html など) であることが必要です。ユーザーが指定した URL がフォルダルックアップに似ている場合は、末尾のスラッシュの有無によって、ウェブサイトエンドポイントの動作が決まります。たとえば、末尾にスラッシュのある次の URL はphotos/index.html インデックスドキュメントを返します。
https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/IndexDocumentSupport.html
http://bucket-name.s3-website.Region.amazonaws.com/photos/
上記の通り、
- フォルダ構造をバケット内に作成する場合は、各レベルにインデックスドキュメントが存在している必要がある。
- フォルダ内のインデックスドキュメントを探しに行くため、URLの指定は
/フォルダ名
まで指定しないとインデックスドキュメントを見つけることができず以下のように404 Not Foundエラーが返ってきます。

CloudFront OAC(Origin Access Control)とOAI(Origin Access Identity)について
OAI (Origin Access Identity)とは
CloudFront OAIはCloudFront経由でリクエストしているビューワに変わってバケット内のファイルにアクセスすることができます。
-> もう少し簡単に言うと”S3バケット上のコンテンツを署名や期限などの複雑な設定を設けず、CloudFrontからアクセスできる”
CloudFront OAIに対してS3バケット内のファイルへアクセス許可を付与する必要があります。
S3バケット内のファイルへのアクセス許可をするためには、以下のバケットポリシーを付与する必要があります。
Principal: "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity <origin access identity ID>"
上記を指定することでS3内のファイルにアクセス (このポリシーはGetObjectなので読み取り)することをOAIに委任する、という意味になります。
OAIのIDを確認するためにはCloudFrontコンソールのARNを確認する必要があります。
参考
バケットポリシーの全体は以下です。
{
"Version": "2012-10-17",
"Id": "PolicyForCloudFrontPrivateContent",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity <origin access identity ID>"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::<S3 bucket name>/*"
}
]
}
書き込み許可も付与したい場合は、s3:PutObject
をActionに付与することで、書き込みすることが可能です。
{
"Version": "2012-10-17",
"Id": "PolicyForCloudFrontPrivateContent",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity <origin access identity ID>"
},
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::<S3 bucket name>/*"
}
]
}
OAIの特徴
※AWS KMSとはデータを保護するために暗号化キーを作成し、そのキーを使用して、対象のAWSリソースにアクセス許可を求めるもの


AWS Key Management Serviceキーで SSE-KMSでサーバー側の暗号を使ってデータを保護します。
サーバー側の暗号化とは、データを受信するアプリケーションまたはサービスによって、送信先でデータを暗号化することです。
S3の暗号化について深堀
S3におけるデータ暗号化について
ここで少し暗号化に関する話が出てきたので、S3におけるデータ暗号化について深堀していきたいと思います。
S3にはいくつかの暗号化の仕組みを備えています。
サーバーサイドの暗号化 (SSE: Server Side Encryption)
オブジェクトをS3へ保存する前に暗号化します。
オブジェクトをダウンロードするときに復号します。
サーバーサイドでの暗号化はデフォルトで設定されます。
メリット: カスタマー側で暗号化処理の仕組みを実装しなくても済みます。

クライアントサイドの暗号化 (CSE: Client Side Encryption)
クライアント側で暗号化し、S3へアップロードします。
暗号化の仕組みをクライアントサイドで用意する必要があります。
データが暗号化された状態でネットワーク経路で転送されるため、S3はデータ転送時における暗号化もS3へデータ書き込みする際の暗号化、データにアクセスしてダウンロードする際の復号には関与しません。

データ転送中に暗号化

S3の暗号化方法は3つ存在
SSE-S3: Amazon S3マネージドキーによるサーバー側の暗号化
AWS公式引用
Amazon S3 では、Amazon S3 のすべてのバケットに対する基本レベルの暗号化として、Amazon S3 マネージドキーによるサーバー側の暗号化 (SSE-S3) が適用されるようになりました。2023 年 1 月 5 日以降、Amazon S3 にアップロードされるすべての新しいオブジェクトは、追加費用なしで、パフォーマンスに影響を与えずに自動的に暗号化されます。S3 バケットのデフォルト暗号化設定と新しいオブジェクトのアップロードのための自動暗号化ステータスは、AWS CloudTrail ログ、S3 インベントリ、S3 ストレージレンズ、Amazon S3 コンソール、および AWS Command Line Interface と AWS SDK の追加の Amazon S3 API レスポンスヘッダーとして利用できるようになりました。詳細については、「デフォルトの暗号化のよくある質問」を参照してください
Amazon S3によって管理される一意のキーを使って暗号化します。
暗号化規格はAES-256
(共通鍵暗号方式 : 対称鍵)を使用して暗号化します。
x-amz-server-side-encryption: オブジェクトを格納するときに付与したメタデータ (ドキュメントなどを例にすると、最終更新日などの内部データのことで、いつ、誰が、何を、更新したかが分かるようにしておくデータのこと)を表しています。
この場合、x-amz-server-side-encryption
はヘッダー情報であり、Amazon S3のサーバー側の暗号化を示しています。
“x-amz-server-side-encryption”:”AES-256”
とすることでAmazon S3のサーバーサイドでAES-256という暗号化規格を使って暗号化しますよ、ということを示しています。

次のバケットポリシーを付与することで、x-amz-server-side-encryption
ヘッダーがリクエストに含まれないと、オブジェクトへのアップロードは拒否しますよ、という制御をすることが可能。
{
"Version": "2012-10-17",
"Id": "PutObjectPolicy",
"Statement": [
{
"Sid": "DenyIncorrectEncryptionHeader",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::DOC-EXAMPLE-BUCKET/*",
"Condition": {
"StringNotEquals": {
"s3:x-amz-server-side-encryption": "AES256"
}
}
},
{
"Sid": "DenyUnencryptedObjectUploads",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::DOC-EXAMPLE-BUCKET/*",
"Condition": {
"Null": {
"s3:x-amz-server-side-encryption": "true"
}
}
}
]
}
実際に上記のバケットポリシーを付与して、何も意識せずにS3へファイルをアップロードすると以下にように、アップロードに失敗します。
エラー内容はアクセスが拒否されているため、と出ている通り、”s3:x-amz-server-side-encryption": "AES256"
を指定していないからですね。

では、AWS CLIを使用して”s3:x-amz-server-side-encryption": "AES256"
を指定してファイルをアップロードしてみましょう。
マネジメントコンソールからアップロードしたい場合は以下を参照してください。
aws s3api put-object --bucket DOC-EXAMPLE-BUCKET1 --key object-key-name --server-side-encryption AES256 --body file path
--bucket DOC-EXAMPLE-BUCKET1
--body file path
上記は自身の環境に置き換えて変更してください。
--key object-key-name
上記はファイル名を指定する -> index.htmlの場合はindex.html
と指定
上記コマンドを実行すると無事にファイルのアップロードをすることができました。

SSE-KMS: AWS Key Management Service に保存されているKMSキーによるサーバ―側の暗号化
"x-amz-server-side-encryption":"aws:kms"
ヘッダーを指定します
次のバケットポリシーを付与することで、"s3:x-amz-server-side-encryption":"aws:kms"
ヘッダーがリクエストに含まれないと、オブジェクトへのアップロードは拒否しますよ、という制御をすることが可能。
{
"Version":"2012-10-17",
"Id":"PutObjectPolicy",
"Statement":[{
"Sid":"DenyUnEncryptedObjectUploads",
"Effect":"Deny",
"Principal":"*",
"Action":"s3:PutObject",
"Resource":"arn:aws:s3:::DOC-EXAMPLE-BUCKET1/*",
"Condition":{
"StringNotEquals":{
"s3:x-amz-server-side-encryption":"aws:kms"
}
}
}
]
}
SSE-C: クライアント (お客さん)が指定したキーによるサーバ側の暗号化
SSE-Cを使用する場合はAmazon S3はHTTP経由で行われたリクエストは全て拒否されます。
HTTPSを利用する必要があります。
次のバケットポリシーを付与することで、"x-amz-server-side-encryption-customer-algorithm"
ヘッダーがリクエストに含まれないと、オブジェクトへのアップロードは拒否しますよ、という制御をすることが可能。
{
"Version": "2012-10-17",
"Id": "PutObjectPolicy",
"Statement": [
{
"Sid": "RequireSSECObjectUploads",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::DOC-EXAMPLE-BUCKET/*",
"Condition": {
"Null": {
"s3:x-amz-server-side-encryption-customer-algorithm": "true"
}
}
}
]
}
OAC (Origin Access Control)とは
それではOACの話に行きます。
OAIで制限されていたAWS KMS
と SSE-KMS
のサポートが行われていることです。
SSE-KMSで暗号化されているS3バケットに対してもデータのダウンロード、アップロードができます。
サポートしている内容は以下の通りです。
すべての AWS リージョンのすべての Amazon S3 バケット (2022 年 12 月以降に開始されたオプトインリージョンを含む)
AWS KMS による Amazon S3 サーバー側の暗号化 (SSE-KMS)
Amazon S3 に対する動的なリクエスト (PUT と DELETE)
AWS公式 Amazon S3 オリジンへのアクセスの制限
OAC handson
それではCloudFront OACを経由してS3バケット内のオブジェクトにアクセスして、先ほどのindex.htmlを表示する手順をハンズオンしていきましょう。
S3 handsonの中で全世界へのアクセスを許可していましたが、S3への直接のアクセスを防ぐため、パブリックアクセスを拒否するように戻します。
バケット名を選択し、アクセス許可のタブを選択します。

ブロックパブリックアクセス (バケット設定)の編集をクリックします。

パブリックアクセスを全てブロックにチェックを入れ変更の保存をします。

CloudFrontディストリビューションを作成します。
CloudFrontの画面を開き、ディストリビューションの作成をクリックします。

オリジンドメインは作成したS3バケットを選択します。

オリジンアクセスをOrigin access control settings (recommended)
を選択します。

Origin access controlからコントロール設定を作成を選択します。

Create control settingでOACを作成します。

WAFは今回使用しないのでセキュリティ保護を有効にしないにチェックをいれます。

これでディストリビューションを作成します。
作成が完了すると以下のように、S3バケットポリシーを更新する必要があります、というメッセージが表示されるので、ポリシーをコピー
をクリックして、S3バケットの権限に移動してポリシーを更新する
をクリックします。

CloudFront OACからS3バケットへアクセスするバケットポリシーを付与します。
以下のポリシーを付与することで、CloudFrontに対してS3バケット内への読み取りアクセスを許可する権限を付与できます。
{
"Version": "2008-10-17",
"Id": "PolicyForCloudFrontPrivateContent",
"Statement": [
{
"Sid": "AllowCloudFrontServicePrincipal",
"Effect": "Allow",
"Principal": {
"Service": "cloudfront.amazonaws.com"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::dev-s3-content/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::xxxxxxxx:distribution/EISYxxxxxx"
}
}
}
]
}
CloudFront経由でアクセス
CloudFrontディストリビューション名/ディレクトリ名/index.htmlでアクセスすると、以下のようにアクセスすることができました。

まとめ
CloudFront OACを経由してS3バケット内にあるオブジェクトにアクセスする方法についてまとめてました。
S3にはデータ暗号化に関する部分がかなり奥が深いと感じており、特にSSE-KMSについてはまだまだ理解が浅いと感じておりますので、その点については別途また深堀する記事を書いていきたいと思います。
また、SSE-KMSを使用したCloudFront OACの作成についても併せて理解を深める必要があると感じております。
参考書籍
DNSがよくわかる教科書
Amazonで株式会社日本レジストリサービス(JPRS) 渡邉結衣、佐藤新太、藤原和典, 森下泰宏のDNSがよくわかる教科書。アマゾンならポイント還元本が多数。株式会社日本レジストリサービス(JPRS) 渡邉結衣、佐藤新太、藤原和典, 森下泰宏作品ほか、お急ぎ便対象商品は当日お届けも可能。またDNSがよくわかる教科書もアマゾン配送商品なら通常配送無料。