【AWS】0円から始めるWebサイトのセキュリティ
─ WAF の必要性と無料でできる対策
「静的サイトなら攻撃されにくいでしょ?」──そう思っていませんか?
実は公開直後から世界中のボットによるスキャンや攻撃リクエストが飛んできます。
この記事では、AWS上でS3とCloudFrontを使った個人ブログ向けに:
- WAFが防げる攻撃とその料金
- 0〜数十円で実践できる無料対策
- 具体的な設定手順(すぐ使えるコード付き)
- WAFを導入すべきタイミング
を初心者向けにやさしくまとめました。
結論:手軽なのはWAF導入。無料枠を活用すればほぼ無料でも十分守れます。
1. WAFとは?─役割・防げる攻撃・料金
1-1. WAFの役割
WAF (Web Application Firewall)は、Webサーバーへ届くHTTPリクエストの中身を検査し、不正を門前払いする「荷物検査付きゲート」です。
AWS WAFをCloudFront前段に置くと、世界225以上のPoP(配信拠点)で即ブロックできます。
1-2. 防げる代表的な攻撃
- EDoS(コスト増大攻撃)
- インジェクション(SQLi/XSS)
- SSRF(サーバーサイドリクエストフォージェリ)
- ディレクトリトラバーサル
- Hostヘッダー改ざん
- ボット/スクレイピング
- Slowloris(スローリクエスト)
- 既知脆弱性エクスプロイト
- ログイン総当たり(ブルートフォース)
1-3. WAFの料金
項目 | 月額 | 説明 |
---|---|---|
Web ACL | 約¥750 | サイトごとに1つ |
カスタムルール (×2) | 約¥300 | 2本で十分 |
リクエスト検査 (100万回) | 約¥90 | アクセス数に比例 |
合計例 | 約¥1,140 | 月100万PVの場合 |
- 月10万PV → 約¥230
- 月30万PV → 約¥460
- 月100万PV → 約¥1,140
2. WAFを使わず「ほぼ無料」で守る考え方
ポイントは 「動的コードを置かない」 + CloudFrontの無料機能 の活用です。
- 静的サイトジェネレータでHTMLを生成、ユーザー入力フォームを置かない
- S3バケットはパブリックアクセスブロック + CloudFront OACで非公開
- CloudFrontでHTTPS強制・ヘッダー最小転送・アイドルタイムアウトを設定
robots.txt
とCloudFront Functionsで悪質UAをブロック
3. 攻撃別:無料でできる対策と手順
3.1 EDoS(コスト増大攻撃)
概要:攻撃者がCloudFrontやS3に大量リクエストを送りつけ、従量課金モデルの穴を突いて請求額を跳ね上げる手口です。単なるサービス停止(DoS)ではなく、運営を金銭的に追い詰める“Economic Denial of Sustainability”攻撃です。
対策:以下の二段構えで“金銭的防御”を実現します。
- コスト監視:CloudWatch Billing Alarm/AWS Budgets で請求額を常時モニタリング
- 自動カットオフ:しきい値超過通知 → SNS → Lambda 自動実行 → CloudFront 停止 & S3 ポリシー全拒否
- 予防策:WAF のレートベースルールで異常大量アクセスを事前にブロック
手順:
- 請求メトリクスの有効化
コンソール → Billing and Cost Management に移動 → 左メニューバーから請求設定 → アラーと設定を編集して「CloudWatch 請求アラートを受信する」 - Billing Alarm の作成
CloudWatch → アラーム → アラームの作成 → メトリクスを選択 → Billing → 概算合計請求額でUSDを選択 → 静的しきい値を>にして50ドル程度を入力 - アクションの設定
アラーム状態を選択 → 新しいトピックの作成から自分のメールアドレスを登録(メールで確認) → 一旦次へ → アラーム名を入力し作成 - Lambda 関数実装
Lambdaをコンソールから起動 → 関数の作成 → 一から作成 → 関数名「CostCutoffFunction」 → コードソースに以下のコードを入力(DIST_IDとBUCKET_NAMEは自分のもの) - アラーム→Lambda連携
作成したアラームの編集 → Lambdaアクションを指定し、アラーム発火時に自動実行。 - HugoやJekyllなどの静的サイトジェネレータのテンプレートで自動エスケープ(例:
{{ .Content | htmlEscape }}
)をONにする。 - コメント欄やフォームなど、動的なユーザー入力機能を一切設置しない。
- CIツールでHTML構造のLintを導入し、
<script>
タグ混入などを検知する。 - JavaScriptから呼び出すAPIエンドポイントを1つに絞り、パラメータを受け付けない固定URL設計にする。
- CloudFrontのレスポンスヘッダー設定で、
Access-Control-Allow-Origin
を自ドメインのみに制限。 - 動的機能が必要な場合は、Lambda@Edgeなどを経由してドメインホワイトリストを強制する。
- S3バケットのバケットポリシーで
"Action": "s3:ListBucket"
をに設定。 - オブジェクトキーには必ず安全なプレフィックス(例:
posts/
)のみを使用。 - 「../」を含むリクエストはCloudFront Functionsでステータス403にリジェクトするコードを追加してもOK。
- CloudFrontのキャッシュポリシーでHeader Forwardingを「Whitelist」に設定し、
Host
以外は転送しない。 - ACM証明書で独自ドメインを設定し、HTTP→HTTPSリダイレクトを強制。
- CloudFront FunctionsでHostヘッダー不一致時に403返却するロジックを追加するとさらに強固。
- ドキュメントルートに
robots.txt
を配置し、User-agent: *
やDisallow: /private/
を設定。 - CloudFront Functionsを作成し、Viewer Requestイベントで
event.request.headers['user-agent']
をチェック。 - 悪質と判断した場合は
{ statusCode:403 }
を返すロジックを実装し、Behaviorにアタッチ。 - CloudFrontのViewer接続アイドルタイムアウトを標準30秒のまま運用。
- 必要に応じてオリジンのIdle Timeoutも短く調整。
- バックエンドを持たない静的サイト構成にする。
- ビルド環境で
npm audit
やDependabotを使い、脆弱性を自動修正。 - AWS VPNまたはPrivate Hosted Zone経由で管理画面のEndpointを限定。
- パブリックDNSには管理Endpointを登録せず、外部から到達不可に設定。
// CloudFront 停止
const cf = new AWS.CloudFront();
const cfg = await cf.getDistributionConfig({Id:DIST_ID}).promise();
await cf.updateDistribution({
Id: DIST_ID,
IfMatch: cfg.ETag,
DistributionConfig: {...cfg.DistributionConfig, Enabled: false}
}).promise();
// S3 全拒否
const s3 = new AWS.S3();
await s3.putBucketPolicy({
Bucket: BUCKET_NAME,
Policy: JSON.stringify({
Version: "2012-10-17",
Statement:[{
Effect:"Deny",
Principal:"*",
Action:"s3:GetObject",
Resource:`arn:aws:s3:::${BUCKET_NAME}/*`
}]
})
}).promise();
3.2 インジェクション/XSS・SQL
概要:ユーザー入力を通じて悪意あるスクリプト(XSS)やSQL文(SQLインジェクション)が実行され、情報漏洩や画面改ざんを招く攻撃です。
対策:お問い合わせフォームや検索などの入力フォームを置かない。(もっとも簡単)
手順:
3.3 SSRF(サーバーサイドリクエストフォージェリ)
概要:サーバーが攻撃者指定の外部・内部ネットワークにリクエストを送らされ、バックエンド資源が悪用される攻撃です。
対策:フロントエンドで呼び出すAPIを固定し、CORS設定で許可ドメインのみを許可します。
手順:
3.4 ディレクトリトラバーサル
概要:「../
」などで本来公開すべきでないファイルへアクセスし、機密情報を取得される攻撃です。
対策:S3のキー正規化機能とバケットポリシーでListBucket権限を拒否します。
手順:
3.5 Hostヘッダー改ざん
概要:偽のHostヘッダーを使い、キャッシュポイズニングや不正リダイレクトを引き起こす攻撃です。
対策:CloudFrontのCache Policyで転送するヘッダーを最小限に絞り、HTTPS強制でHost一致を担保します。
手順:
3.6 ボット/スクレイピング
概要:悪質なクローラーやスクレイパーが大量リクエストを送り、コンテンツ盗用や帯域・キャッシュ浪費を引き起こします。
対策:robots.txt
で一般クローラーを誘導し、CloudFront Functionsで悪質User-Agentをフィルタリング。
手順:
3.7 Slowloris(スローリクエスト)
概要:少量のリクエストを長時間にわたり維持し、同時接続数を枯渇させる攻撃です。
対策:CloudFrontのデフォルトIdle Timeout(30秒)を利用して長時間接続を自動切断。
手順:
3.8 既知脆弱性エクスプロイト
概要:Log4Shellなど公開済みの脆弱性を狙い、攻撃コードを実行する手法です。
対策:サーバーサイドフレームワークを使わず、依存ライブラリはCIで常に最新化。
手順:
3.9 ログイン総当たり(ブルートフォース)
概要:APIや管理画面への多数リクエストで認証を突破しようとする攻撃です。
対策:管理画面はVPC内やVPN経由のみアクセス可とし、パブリックに公開しない。
手順:
3.10 攻撃別対策のまとめ
攻撃 | 無料対策 | 手順概要 |
---|---|---|
EDoS(コスト増大攻撃) | CloudWatch Billing Alarm+Lambdaで自動カット | アラームを設定し、自動的にコストをカットするように |
インジェクション/XSS | ユーザー入力排除+自動エスケープ | 静的サイト生成時にテンプレート自動エスケープON |
SSRF | API呼び出し先固定+CORS制限 | JS固定URL + CloudFrontで許可ドメインのみ制御 |
ディレクトリトラバーサル | ListBucket拒否 | S3バケットポリシーでListBucketをDeny |
Hostヘッダー改ざん | ヘッダー最小転送+HTTPS強制 | Cache PolicyでHostのみ許可 |
ボット/スクレイピング | CloudFront FunctionsでUAフィルタ | Viewer RequestにUAチェックFunctionを適用 |
Slowloris | Idle Timeout自動切断 | CloudFrontの30秒アイドルタイムアウトを利用 |
既知脆弱性エクスプロイト | 静的構成+ライブラリ最新化 | ビルド環境でnpm audit/Dependabotを導入 |
ブルートフォース | 管理画面非公開 | VPC/VPN内のみアクセス可に設定 |
4. すぐ使えるコード例(CloudFront Functions)
// blockBadBots.js
function handler(event) {
var ua = event.request.headers['user-agent']?.value || '';
if (ua.match(/curl|python|BadBot|crawler|scraper/i)) {
return { statusCode: 403, statusDescription: 'Forbidden' };
}
return event.request;
}
- CloudFront → 関数 → 関数を作成 → 名前をblockBadBotsなどに
- コードを貼り付け、発行
- ディストリビューションのビヘイビアへ → 編集を押し、ビュワーリクエストに関数を選択
5. WAFを導入するタイミングの目安
- 広告収益¥750〜1,500以上
- コメント欄や検索などユーザー入力を追加したい
- 企業案件でSLAを求められる
月約¥1,140のWAFは“安い保険”です。
6. まとめ
- 静的ブログは0円でも多くの攻撃を防げる
- 入力フォーム追加=リスク急増 → その時点でWAF導入を検討
- CloudFront Functionsなど無料枠を駆使し、安全なサイト運営を楽しむ