1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

CloudFront + WAFでS3静的サイトを高セキュリティ運用する方法

Last updated at Posted at 2025-10-16

はじめに

前回の記事(https://qiita.com/cho-tehu/items/555e9dc7ec08cbc3330d) では、S3単体で静的サイトをホスティングし、バケットポリシーで特定IPからのみアクセスを許可する方法を紹介しました。
今回はさらに一歩進んで、CloudFront + WAF を組み合わせて「HTTPS対応」「グローバル配信」「高度なアクセス制御」を実現する手順を紹介します。

なぜCloudFront + WAFなのか

S3単体ホスティングは簡単で低コストですが、次のような制約があります:

  • HTTPS(SSL)非対応(HTTPのみ)

  • IP制限はできるが、国別や攻撃検知などの柔軟な制御は不可

  • キャッシュや高速配信ができない

CloudFront + WAFを組み合わせることで、これらをすべて解消できます。

S3単体と CloudFront + WAF の違い

項目 S3単体 CloudFront + WAF
アクセス制御 バケットポリシーでIP制限のみ WAFで複雑な条件(IP・国・ボットなど)を設定可能
HTTPS CloudFrontなしでは使えない 自動でHTTPS化(無料のACM証明書対応)
キャッシュ なし CloudFrontがCDNとしてグローバルキャッシュ
パフォーマンス S3リージョンに依存 世界中のエッジロケーションから高速配信
セキュリティ IP制限・署名付きURL程度 DDoS防御・SQLi/XSS対策・Bot制御可能
コスト 安い(数円~) 若干高い(数十円~百円単位)
運用規模 小規模向け 中〜大規模・社内システム・商用向け

手順

1. S3バケットを作成

スクリーンショット 2025-10-15 234418.png

設定はデフォルトで良いです。
image.png

image.png

スクリーンショット 2025-10-15 234657.png

2. ファイルをアップロード

配信する静的ウェブサイトのファイルをアップロードします。
前回と同様に、Reactのデフォルトアプリケーションを配置します。

Reactのデフォルトアプリケーションを作成

npx create-react-app my-app

ビルドして静的ファイルを作成

cd my-app
npm run build

build/配下のファイルをS3バケットにアップロード

スクリーンショット 2025-10-08 020043.png

全選択してドラッグアンドドロップ
スクリーンショット 2025-10-08 015817.png

スクリーンショット 2025-10-08 020240.png

3. CloudFrontディストリビューションを作成

スクリーンショット 2025-10-15 235412.png

スクリーンショット 2025-10-15 235705.png

S3 Origin に 1で作成したバケットのARNを指定する。
スクリーンショット 2025-10-16 000004.png

作成時点でオリジンアクセス制御(OAC)が有効化されていると思いますが、下記の流れで念のため確認
(この設定でS3バケットがCloudFront経由のアクセス以外を拒否するようになります)
スクリーンショット 2025-10-16 225539.png

スクリーンショット 2025-10-16 225855.png

S3の当該バケットのアクセス許可のタブからバケットポリシーが更新されているかも確認
スクリーンショット 2025-10-16 234230.png

{
  "Version": "2008-10-17",
  "Statement": [
    {
      "Sid": "AllowCloudFrontServicePrincipalReadOnly",
      "Effect": "Allow",
      "Principal": {
        "Service": "cloudfront.amazonaws.com"
      },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::test-bucket/*",
      "Condition": {
        "StringEquals": {
          "AWS:SourceArn": "arn:aws:cloudfront::<アカウントID>:distribution/<ディストリビューションID>"
        }
      }
    }
  ]
}

Resource に指定するのはS3バケットのARN
スクリーンショット 2025-10-16 234746.png

SourceArnに指定するのはCloudFrontのARNです。
スクリーンショット 2025-10-16 234940.png

次に、デフォルトルートオブジェクトの設定を行います。
(デフォルトルートオブジェクトとはウェブサイトのトップページ(ホーム)として表示されるファイル名のことです)
スクリーンショット 2025-10-16 235237.png

index.html に変更して保存する。
スクリーンショット 2025-10-16 235612.png

4. WAFの設定

デフォルトでは下記のルールが適用されていますが、必要に応じて追加してください。

  • AWS-AWSManagedRulesAmazonIpReputationList
  • AWS-AWSManagedRulesCommonRuleSet
  • AWS-AWSManagedRulesKnownBadInputsRuleSet

追加方法

スクリーンショット 2025-10-17 000554.png

スクリーンショット 2025-10-17 000643.png

代表的なルールセット

ルール名 対応する脅威・特徴 説明
AWSManagedRulesCommonRuleSet 一般的な脆弱性 最も基本的なルールセット。XSS、SQLi、ディレクトリトラバーサルなどを防御します。
AWSManagedRulesKnownBadInputsRuleSet 危険な入力パターン 既知の悪意あるリクエスト(例:不正なヘッダやメタ文字列)をブロックします。
AWSManagedRulesAmazonIpReputationList 悪意あるIPアドレスからのアクセス AWSが管理する「不正アクセス元」リストを基に、悪評高いIPを自動でブロックします。
AWSManagedRulesSQLiRuleSet SQLインジェクション データベースを狙う攻撃(OR 1=1 など)を検知・遮断します。
AWSManagedRulesBotControlRuleSet Botアクセス スクレイピングや自動化ツールによるアクセスを制限します。

これらを組み合わせることで、CloudFront + WAF 構成で 多層的なセキュリティ防御が実現できます。

AWS WAF の課金対象

  • Web ACL(アクセス制御リスト)
    月額料金:1 Web ACL ごとに固定料金が発生
    例:$5〜\$10/月(リージョンや AWS マネージドルールの有無で変動)

  • ルールの課金
    ルールの種類によって料金が異なる

    • AWS マネージドルール(AWS 提供のルールセット)
      ルールごとに月額課金($1〜\$3/ルール/月程度)
    • カスタムルール(自分で作成した条件)
      ルールごとに月額課金($1/ルール/月程度)
  • リクエスト数による課金
    Web ACL 配下で処理された HTTP リクエストごとに課金
    例:$0.60/100 万リクエスト(リージョンによって変動)

※ 時間単位で計算されるので、1時間利用した場合は その月の日数 × 24時間 で割った金額です。

5. ブロックパブリックアクセスの無効化

スクリーンショット 2025-10-16 232318.png

スクリーンショット 2025-10-16 232346.png

6. 念のためキャッシュクリア

S3 の中身を更新した場合は、キャッシュクリアを行わないと反映されません。

スクリーンショット 2025-10-17 001746.png

こちらの例では /* を指定し、すべてのファイルのキャッシュをクリアしています。
/index.html など指定すれば、特定のファイルのみキャッシュクリアすることができます。

スクリーンショット 2025-10-17 001833.png

CloudFrontはS3などの オリジン(元データ) からコンテンツを取得し、エッジロケーション(世界中のキャッシュサーバー)にキャッシュを保存して配信します。
このキャッシュは以下の条件でしか更新されません。

キャッシュが更新されるタイミング

  • TTL(有効期限)が切れたとき
    CloudFrontはオブジェクトをキャッシュした際に「TTL(Time To Live)」を設定します。TTLが切れると次のリクエストで新しいバージョンをオリジンから取得します。
    TTLはHTTPヘッダの Cache-Control または Expires、またはCloudFrontのキャッシュポリシーで設定可能です。
  • 手動で「無効化(Invalidation)」を実行したとき
    管理コンソールやCLIからCreateInvalidationを行うことで、指定したパスのキャッシュを強制的に削除し、次のアクセス時に最新データを取得できます。
  • オブジェクトの名称を変えたとき
    S3のファイル名を変更した場合、CloudFrontは別オブジェクトとして扱うため自動的に新しいキャッシュを作ります。
  • オリジン側の設定でキャッシュ無効化を制御したとき
    S3やAPIのレスポンスでCache-Control: no-cacheなどを指定すると、CloudFrontは毎回オリジンに確認します。

7.動作確認

ディストリビューションドメイン名をコピーしてブラウザからアクセスする。

スクリーンショット 2025-10-17 002902.png

image.png

最後に

ドメイン買うと結構お金かかるんで、カスタムドメインの設定まで紹介することができませんでした。
ちなみに、ここまでセキュリティ固めて、個人用に配信する人いないと思いますが、WAFにカスタムルールで任意のIPを許可するルールとすべてのIPを拒否するルールを追加すれば、特定IPのみ許可する設定も可能です。
※ 必ず任意のIPを許可するルールを先に設定してください (順序が先のルールが優先されるため)。

要素が結構多くて散らかってしまいそうなので、細かい設定かなり無視してしまいました。
気になることがあったらコメントをお願いいたします。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?