Skip to content

FDサービス系 ALB + WAF 移行設計書

ドキュメント情報

項目内容
作成日2026-05-22
対象環境production (*.fstdr.jp, *.fastdoctor.jp)
対象システムFDサービス系ALB(CloudFlare脱却対象)
関連Issuemental-online-karte#11657, mental-online-karte#11666, mental-online-karte#11667
関連ドキュメントCloudFront WAF アーキテクチャ設計書(LP向け), WAF 運用設計書(LP向け), FDサービス系WAF 運用設計書, 構成比較検討結果

本設計書の位置づけ

  • 最優先はセキュリティリスクの軽減。現在CloudFlare Proxyを外した状態でWAF保護がなく、この状態の解消が急務
  • ALB + WAF(REGIONAL)構成を採用する。当初は CloudFront + WAF で全サービスを統一する方針だったが、staging 検証で CloudFront 固有の制約がサービス間通信と競合する問題が判明したため、方針を変更した(詳細は構成比較検討結果を参照)
  • CloudFront のキャッシュによるパフォーマンス向上・コスト削減は今後の検討課題として残す。WAF 導入が安定した後、必要なサービスから CloudFront の追加を検討する
  • ルール構成・閾値・Phase計画等の具体値は 初期設定値 であり、Count運用中の観察結果やサービス固有の事情に応じて変更していく前提
  • 設計書自体も運用の中で更新し、実態と乖離しないように維持する

要するに: まずCountで入れる → 観察する → 育てていく。この設計書はそのスタート地点を定義するもの。


背景

障害の経緯(2026-05-21)

  • 21:40頃、CloudFlare → ALB 経路上でレイテンシが異常悪化し、FDサービス全体に影響
  • 止血対応としてCloudFlareのProxy設定をOFF(DNS onlyに切替)→ 順次復旧
  • 恒久対応方針: CloudFlare Proxyは戻さず、AWS WAF へ完全移行する

目的

  • CloudFlareへの依存を排除し、AWS内で完結するセキュリティレイヤーを構築する
  • WAFによる基本的な攻撃防御を実現する(導入を優先し、段階的に強化)

なぜ ALB + WAF(REGIONAL)にするのか

当初は CloudFront + WAF で全サービスを統一する方針だったが、staging 検証で CloudFront 固有の制約(GET + body → 403 等)がサービス間通信と競合する問題が判明。CloudFront Functions / Lambda@Edge での回避も不可能であることを確認し、ALB に直接 WAF をアタッチする構成に変更した(API Gateway(HTTP API)は WAF 非サポートのため別途検討)。

  • セキュリティリスクの軽減が最優先: WAF 保護がない状態の解消が急務。ALB + WAF なら迅速に導入できる
  • CloudFront 固有の制約を回避: 既存アプリとの互換性問題のリスクがない
  • バイパス耐性が構造的に高い: WAF が ALB に直接統合されるため、バイパス不可能(検証済み・SA 確認済み)
  • CloudFront のキャッシュは今後の検討課題: WAF 導入が安定した後、必要なサービスから CloudFront の追加を検討する

詳細な比較検討・経緯: CloudFront + WAF vs ALB + WAF 構成比較・検討結果を参照

参考ドキュメント

Cloudflare設定との対応

CloudFlareで有効化されていた保護機能と、AWS WAFでの対応方針を整理する。 LP向け設計書の対応表も参照。

Cloudflare機能CloudFlareでの状態AWS WAF相当本設計での対応備考
OWASP コア✅ 有効(ブロック)Core Rule Set (CRS)Phase 1 Count → Phase 2 BlockOWASP Top 10 対策
Cloudflare Managed Ruleset✅ 有効IP Reputation + Known Bad InputsPhase 1 Count → Phase 2 Block2つのマネージドルールで同等カバー
IP Access Rules推定有効IP Set(共有 Blacklist)Phase 1 から即Block既存 waf-shared-sets モジュール活用
HTTP DDoS保護(自動)デフォルト有効Shield Standard(自動)+ Rate-based rulesShield Standard はデフォルト有効。Rate-based は Phase 2で検討Shield Standard は L3/L4 中心。アプリ層flood対策として Rate-based ルールをPhase 2で検討
Exposed Credentials Check✅ 有効❌ AWS WAF に直接相当なし対応なしAWS WAF Account Takeover Prevention($10/月/ACL)で部分的に代替可能だが、現時点では未導入。Rate-based ルールでブルートフォースは緩和
スーパー ボット ファイト モード✅ 有効Bot Control($10/月/ACL)Phase 3以降で検討まずマネージドルール+Rate-based でBot対策し、不足なら導入判断
スキーマ検証✅ 有効❌ AWS WAF に相当機能なし対応なし(アプリ層で対応)APIスキーマ検証はアプリケーション側(API Gateway, アプリコード)で実施

CloudFlareになかった追加保護(本設計で新規導入):

機能AWS WAF ルール導入Phase追加理由
Geo制限(日本以外ブロック)Geo Match (Custom)導入しない海外勤務の医師・渡航中の患者向けサービスがあるため
Anonymous IP ListAWSManagedRulesAnonymousIpListPhase 1 Count → Phase 2 BlockTOR/VPN/Hosting Provider 経由の攻撃を遮断
SQLi専用ルールAWSManagedRulesSQLiRuleSetPhase 1 Count → Phase 2 BlockRDS利用サービス向け。CRSのSQLi検出を補完
Rate-based(脅威IP向け)Rate-based + Label scope-downPhase 2以降で検討IP Reputation の誤検知リスクを観察してから判断

方針: CloudFlareで有効だった保護は全てAWS WAFで再現する。 さらに、CloudFlareでは未設定だった Anonymous IP List・SQLi・Rate-based を追加し、 CloudFlare時代よりも強固な保護を実現する。 一方、AWS WAFに直接相当しない Exposed Credentials Check・スキーマ検証は、 意図的に対応しないものとして明記する。


対象ドメイン・ALB一覧

障害時に影響を受けたドメイン(CloudFlare Proxy OFF済み):

fd-prod アカウント(967691968827)— *.fstdr.jp

ドメインサービスOrigin(ALB/API GW)AWSアカウントリージョン備考
mental-reserve-patient.fstdr.jpmental-reserve-patientmental-reserve-patient-1989296911.us-east-1.elb.amazonaws.comfd-produs-east-1メンタル予約画面
p.fstdr.jponline-patient-mypageonline-patient-mypage-1403221813.us-east-1.elb.amazonaws.comfd-produs-east-1患者マイページ
mental-reserve-admin.fstdr.jpmental-reserve-adminmental-reserve-admin-1313503852.us-east-1.elb.amazonaws.comfd-produs-east-1メンタル管理画面
fd-app-bff.fstdr.jpfd-app-bfffd-app-bff-1521485696.us-east-1.elb.amazonaws.comfd-produs-east-1アプリBFF
fd-video.fstdr.jpfd-videoonline-video-2038185983.us-east-1.elb.amazonaws.comfd-produs-east-1ビデオ通話
verify-account.fstdr.jpverify-accountverify-account-service-bff-1598402994.us-east-1.elb.amazonaws.comfd-produs-east-1認証
backend-online-karte.fstdr.jpbackend-online-karteonline-karte-service-2073905677.ap-northeast-1.elb.amazonaws.comfd-prodap-northeast-1カルテバックエンド
payment.fstdr.jppaymentd-sslgqoucs8.execute-api.us-east-1.amazonaws.comfd-produs-east-1決済(API Gateway、SG影響なし)
dr-site.fstdr.jpdr-sited-1ef8u9oft5.execute-api.us-east-1.amazonaws.comfd-produs-east-1往診用(API Gateway、SG影響なし)
u.fstdr.jpHerokuencircled-cat-9nts27s1e0zpm2tywaynfes1.herokudns.com-HerokuSMS短縮URL → PMP

fd-sys アカウント(913831226605)— *.fastdoctor.jp / member.fstdr.jp

ドメインサービスOrigin(ALB/API GW)AWSアカウントリージョン備考
backend.fastdoctor.jpfastdoctor-managerfastdoctor-manager-prd-app-383969541.us-east-1.elb.amazonaws.comfd-sysus-east-1FDsysのAPI
cp.fastdoctor.jpfastdoctor-managerfastdoctor-manager-prd-app-383969541.us-east-1.elb.amazonaws.comfd-sysus-east-1クリニックポータル(業務システム)。backend.fastdoctor.jp と同一 ALB
u.fastdoctor.jpfastdoctor-managerfastdoctor-manager-prd-app-383969541.us-east-1.elb.amazonaws.comfd-sysus-east-1短縮URL / リダイレクトサービス。backend.fastdoctor.jp と同一 ALB。トラフィック僅少
cable.fastdoctor.jpcable-fdmcable-fdm-prd-app-1384687700.us-east-1.elb.amazonaws.comfd-sysus-east-1
member.fstdr.jpmemberd-u35c6t4ml1.execute-api.us-east-1.amazonaws.comfd-sysus-east-1会員(API Gateway、SG影響なし)

:

  • API Gateway 経由のサービス(payment, dr-site, member)は全て HTTP API(API Gateway v2)で、HTTP API は AWS WAF をサポートしていない公式ドキュメント)。WAF 導入方法は別途検討が必要。まず ALB のサービスから WAF 導入を優先する
  • *.fastdoctor.jp ドメインは fd-sys アカウントで管理。設計の考え方は同一だが、Terraform管理場所(fastdoctor-manager/)とACM証明書(*.fastdoctor.jp)が異なる
  • member.fstdr.jp はドメインが *.fstdr.jp だが、API GatewayはAWS fd-sysアカウント上に存在する
  • サービス間通信はVPC Peering(fd-prod Virginia ↔ fd-prod Tokyo、fd-prod Tokyo ↔ fd-sys)経由のPrivate通信であり、*.fstdr.jp のinternet-facing ALBをインターネット経由で呼び合う構成にはなっていない(ネットワーク構成図およびルートテーブル確認済み)

アーキテクチャ設計

全体構成

設計方針

項目方針理由
WAF Web ACLサービスごとに個別サービスごとに要件が異なる可能性。コスト: $5/月/ACL
WAFアタッチ先ALB に直接アタッチ(REGIONAL)CloudFront 固有の制約を回避。バイパス耐性が構造的に高い。API Gateway(HTTP API)は WAF 非サポートのため別途検討
WAFスコープREGIONALALB にアタッチするため
IP/UA Blacklist共有waf-shared-sets モジュールを REGIONAL スコープで活用
WAF初期モードCount導入を優先し、誤検知を確認してからBlockへ
Rate-based rulePhase 2で検討誤検知リスクを考慮し、Phase 1のトラフィックパターン観察後に導入判断
Geo制限導入しない海外勤務の医師・渡航中の患者向けサービスがあるため
DNS切替不要CloudFront を使わないため。WAF を ALB にアタッチするだけ
ALB SG制限不要WAF が ALB に直接統合されるため、バイパス不可能(検証済み・SA確認済み)

API Gateway サービスの扱い(別途検討)

payment.fstdr.jp, dr-site.fstdr.jp, member.fstdr.jp は API Gateway(HTTP API)経由。

HTTP API は AWS WAF をサポートしていない公式ドキュメントで確認済み。WAF をアタッチできるのは REST API のみ)。そのため、ALB と同じ方式での WAF 導入はできず、別途検討が必要。

別途検討とする理由:

  • HTTP API で WAF 保護を実現するには CloudFront を前段に置く必要があるが、CloudFront 固有の制約(GET + body → 403 等)の影響調査が必要
  • 認証なしの member.fstdr.jp は攻撃リクエストがバックエンドまで到達するリスクがあり保護の優先度は高いが、構成の検討に時間がかかる
  • セキュリティリスク軽減を最優先するため、まず ALB のサービスから WAF 導入を進める

API Gateway サービスの現状構成:

サービス認証方式バックエンドWAF 保護備考
payment.fstdr.jpLambda Authorizer(API Key)ECS via ALB (VPC Link)別途検討認証ありのため攻撃リクエストは Authorizer で弾かれる
dr-site.fstdr.jpLambda Authorizer(JWT)ECS via ALB (VPC Link)別途検討同上
member.fstdr.jp認証なしECS via ALB (VPC Link)別途検討(優先度高)認証なしのため攻撃リクエストがバックエンドまで到達する

: API Gateway の WAF 導入方法の詳細な比較検討は、ALB の WAF 導入が完了した後に docs/tasks/ に別途ドキュメントを作成して実施する。

コンポーネント詳細

WAFによるアクセス制御の考え方

WAF は ALB に直接アタッチされるため、WAF が検査するのは ALB に接続してきた IP アドレス。 ALB + WAF(REGIONAL)構成では、ALB への全リクエストが WAF を通過する(infra-dev で検証済み、AWS SA 確認済み)。バイパスは構造的に不可能。

エンドユーザー(患者・医師等のグローバルIP)
  → ALB + WAF  ← WAFはALBに接続してきたIP(= エンドユーザーのIP)を検査
  → ECS Tasks(Private Subnet)

バイパス耐性:

  • ECS は全て Private Subnet に配置、assign_public_ip = false(Terraform コードで確認済み)
  • ECS Security Group は ALB Security Group からの通信のみ許可
  • インターネットに公開されている入口は ALB のみ → ALB にアタッチされた WAF を迂回する手段がない

サービスごとのアクセス元とIP制限の可否:

現時点で確実にわかっているアクセス元:

ドメインアクセス元根拠
mental-reserve-patient.fstdr.jp患者(不特定多数)インシデント記録「患者予約」
p.fstdr.jp患者(不特定多数)インシデント記録「患者マイページ」
fd-video.fstdr.jp患者・医師(不特定多数)インシデント記録「FDビデオが重くて入れない(患者も)」
mental-reserve-admin.fstdr.jp社内スタッフ・ゆめみインシデント記録「ゆめみ管理画面」
backend.fastdoctor.jp内部・外部不明インシデント記録「FDsysのAPI」

上記以外のサービスはアクセス元の調査が完了していない。 Phase 1のWAFログ(Countモード)でアクセスパターンを確認した上で、サービスごとのアクセス要件を整理し、IP制限の要否を改めて検討する。

不特定多数のエンドユーザーがアクセスするサービスが大半の見込みであり、IPホワイトリスト方式は取れない可能性が高い。 WAFの防御は複数のルールを組み合わせて攻撃トラフィックを段階的にフィルタするアプローチとなる。具体的なフィルタ方針はアクセス要件の整理後にサービスごとに検討する。

IP制限の方針:

  • 現時点ではTerraform/実機の状態からアクセス元を完全に把握できない
  • Phase 1のWAFログ(Countモード)でアクセスパターンを確認し、サービスごとのアクセス要件を整理した上でIP制限を検討する
  • IP制限はいきなりかけず、徐々に制限する

IPセットの構成:

IPセットスコープ用途
共有IP Blacklistwaf-shared-sets全サービス共通全サービス共通の攻撃元IPブロック攻撃元IP、悪意あるASレンジ(M247等)
共有IP Whitelistforce-allow-ip-list全サービス共通全サービス共通の誤ブロック防止監視ツール、オフィスIP、CI/CD
サービス個別IP Whitelistサービスごとサービス固有のアクセス要件に基づくIP許可paymentの呼び出し元IP等
サービス個別IP Blacklistサービスごと特定IPのみ許可するサービスで、Whitelist以外を全拒否Whitelist方式のサービスで 0.0.0.0/0 をBlock
  • 共有IPセットは waf-shared-sets モジュールで管理(LP向けWAFと共有)
  • サービス個別IPセットは各サービスのWAF Web ACL内で管理(他サービスに影響しない)
  • Phase 1のアクセスパターン確認後に、サービス個別IPセットの要否を判断する

WAFの対象外となる通信:

通信理由
VPC内部通信(ECS → internal ALB等)VPC内のローカルルーティング。CloudFrontを経由しない
VPC Peering経由の通信(fd-prod ↔ fd-sys等)Private Subnet同士のPeering通信。CloudFrontを経由しない
API Gateway VPC Link経由の通信Private通信。CloudFrontを経由しない

WAF Web ACL(サービスごと)

設定項目
ScopeREGIONAL
Default ActionAllow
CloudWatch Metrics有効
Sampled Requests有効
ルール優先度設計(Priority Order)

AWSのベストプラクティスに基づき、コストの低いルール(IP Set等)を先に評価し、WCUの高いマネージドルールに到達するリクエスト数を減らす構成とする。

初期ルール構成(Phase 1):

Priorityルール名タイプアクションWCU備考
0service-allow-ip-listCustom (IP Set)Allow1サービス個別のIP許可リスト(初期は空)
1force-allow-ip-listCustom (IP Set)Allow1共有IP許可リスト(初期は空)
2service-block-ip-listCustom (IP Set)Block1サービス個別のIP拒否リスト(Whitelist方式のサービスで全拒否用。初期は空)
3ip-blacklistCustom (IP Set)Block2共有IP Blacklist(IPv4/IPv6)参照
4ua-blacklistCustom (String Match)Block1共有UA Blacklist参照(sqlmap, nikto, nmap, masscan)
20amazon-ip-reputationManagedCount25AWS脅威インテリジェンス(MadPot)
21anonymous-ip-listManagedCount50TOR, VPN, Hosting Provider IP
30known-bad-inputsManagedCount200Log4j, Spring RCE等の既知脆弱性
40core-rule-setManagedCount700OWASP Top 10対策
50sqli-rule-setManagedCount200SQL Injection 専用ルール

合計 WCU: 1,181 / 1,500(残り319 WCU)

Priority設計の原則AWS公式ガイド):

  1. サービス個別の許可/拒否リスト(最優先)
  2. 共有の許可/拒否リスト(IP/UA)
  3. IP Reputation系(低WCU)
  4. マネージドルール(高WCU、コンテンツ検査)
Phase 1で導入を見送るルール
ルール見送り理由
Rate-based誤検知リスクを考慮し、Phase 1のトラフィックパターン観察後にPhase 2で導入判断
Admin ProtectionFDサービスはカスタムパスのため効果が限定的
Linux/POSIX OSLFI/RFIはCRSでカバー済み。WCUが高く、追加の恩恵が限定的
Bot Controlコストが高い。Phase 3以降で検討

共有リソース(既存の waf-shared-sets モジュール):

  • aws_wafv2_ip_set.shared_ip_blacklist_ipv4 — 共有IPv4ブロックリスト
  • aws_wafv2_ip_set.shared_ip_blacklist_ipv6 — 共有IPv6ブロックリスト
  • ua_blacklist — 共有UAブラックリスト(sqlmap, nikto, nmap, masscan)

WAF Body Inspection(リクエストボディ検査):

  • oversize_handling = "NO_MATCH" を指定し、ボディサイズ上限(デフォルト16KB、最大64KB)を超えるリクエストはルールにマッチしない扱いとして通過させる
  • FDサービスでは画像アップロードやカルテ入力等で大きなPOSTボディが発生するため、ボディサイズ超過時にBlockされないようにする

CRS SizeRestrictions_BODY / SizeRestrictions_QueryString ルールの除外:

  • CRS(AWSManagedRulesCommonRuleSet)の SizeRestrictions_BODY ルールは、リクエストボディが 8,192 バイト を超えると検知する
  • fd-system production の WAF ログ(Phase 1 Count)を分析した結果、管理画面での PATCH 操作(患者データ更新等)が 13,000〜22,000 バイトの JSON ボディを送信しており、Count 検知の 82.6%(1,412/1,710 件) を占めていた
  • cp.fastdoctor.jp(クリニックポータル)でも同様に検知(3日間で579件)。患者情報更新(PATCH /clinic_portal/cp_patients/*.json)、ドキュメントアップロード(POST /clinic_portal/cp_patients/*/cp_documents.json)、一括更新(PATCH /clinic_portal/bulk/cp_patients.json)で 8KB〜24.9MB のボディが発生。cp.fastdoctor.jpbackend.fastdoctor.jp と同一 ALB(fastdoctor-manager-prd-app)のため、WAF 側の SizeRestrictions_BODY rule_action_override(count)は既に適用済み。追加の WAF 設定変更は不要で、Datadog Index Exclusion Filter に cp.fastdoctor.jp を追加するのみ。分析結果
  • p.fstdr.jp(online-patient-mypage)でも同様に検知(5日間で659件、全て COUNT/ALLOW)。トリガーしているAPIは2種類のみ:① PUT /api/ai-triage/session/online_interview/attachment(患者がオンライン問診の事前トリアージで画像を添付アップロード、362件/55%、ボディサイズ 42KB〜4.96MB)、② POST /api/medical_examinations/{id}/kartes/{karteId}/images(診療中に医師・スタッフがカルテに画像を添付、297件/45%、ボディサイズ 33KB〜700KB)。発生時間帯は午前11時〜午後3時(診療時間帯)に集中しており、100% 正規の医療業務パターン。online-patient-mypage は独自 ALB に個別 WAF Web ACL がアタッチされているため、managed_rule_core_rule_set_excluded_rulesSizeRestrictions_BODY を追加する必要がある。分析結果
  • 保険証アップロード等のファイルアップロードでもボディサイズ超過が発生する
  • SizeRestrictions_QueryString ルールは、クエリ文字列が 2,048 バイト を超えると検知する。fd-system の WAF ログ分析(直近5日間)で、医師スケジュール管理(/admin/doctor_work_schedules/*)等の正規業務操作で検知されていた(133件 / 3,400件)
  • これらは正常な業務操作であり、CRS を Block に切り替えた場合に業務に影響する
  • CRS を Block に切り替える際は、SizeRestrictions_BODYSizeRestrictions_QueryStringrule_action_overridecount に維持する
  • count にすることでログは記録されるが、後続ルール(XSS、SQLi 等)の評価はスキップされず正常に機能する(allowterminating action のため後続ルールがスキップされ不可)
  • ログノイズは ログコスト最適化 の方針に沿い、Datadog Index Exclusion Filter で SizeRestrictions_BODY / SizeRestrictions_QueryString の Count ログをインデックスから除外。対象ホスト: backend.fastdoctor.jp, backend-stg.fstdr.jp, cp.fastdoctor.jp, p.fstdr.jp。調査が必要な場合は S3 + Athena で対応
  • u.fastdoctor.jp はトラフィック僅少(3日間で34件)のため Exclusion Filter 対象外
hcl
# CRS を Block に切り替える際の設定例
# SizeRestrictions_BODY / SizeRestrictions_QueryString は Count に維持し、他のルールは Block
managed_rule_group_statement {
  name        = "AWSManagedRulesCommonRuleSet"
  vendor_name = "AWS"
  version     = "Version_1.21"

  rule_action_override {
    name          = "SizeRestrictions_BODY"
    action_to_use { count {} }
  }
  rule_action_override {
    name          = "SizeRestrictions_QueryString"
    action_to_use { count {} }
  }
}

X-Forwarded-For の取り扱い

ALB + WAF(REGIONAL)構成では CloudFront を経由しないため、X-Forwarded-For の hop 数は現状と変わらない。アプリ側の IP 取得ロジックに影響しない。

現状(CloudFlare DNS only):
  Client(203.0.113.5) → ALB
  X-Forwarded-For: 203.0.113.5

ALB + WAF 導入後:
  Client(203.0.113.5) → ALB + WAF
  X-Forwarded-For: 203.0.113.5  ← 変更なし

サービスごとのWAFルール調整方針

WAF ACLはサービスごとに個別だが、初期は全サービス同一のルール構成(上記Phase 1構成)で導入する

Phase 1のCount運用でサービスごとのアクセスパターン・誤検知状況を確認した上で、Phase 2以降で以下を検討する:

  • サービスごとのBlock化タイミングの調整
  • サービス個別IP許可/拒否リストの設定(アクセス要件整理後)
  • 誤検知が多いルールの除外設定(RuleActionOverride または Label-based パターン)
  • CAPTCHA Challenge の導入(ブラウザアクセスのサービスのみ。FE側対応が必要)
  • Rate-basedルールの導入

参考: 誤検知対策として、マネージドルールを Count にしてラベルを付与し、後続の自前ルールでパス単位の除外を行う Label-based パターンがある。 AWS公式ブログ: How to customize behavior of AWS Managed Rules


Rate-based ルール(Phase 2で検討)

Rate-basedルールは一定時間内に同じIPからのリクエスト数が閾値を超えたら自動BlockするAWS WAFの機能。 DDoS/ブルートフォース防御に有効だが、誤検知リスクを考慮しPhase 1では導入せず、トラフィックパターンを観察してからPhase 2で導入判断する

背景: 認証なしのエンドポイントに対してスクリプトが無限にリクエストを送信し、staging環境に障害が発生したインシデントがあった。同じIPからの異常なリクエスト量を検知してBlockする仕組みが必要であり、アプリケーション層ではなくネットワーク層(ALB + WAF)での対策が望ましい。

Phase 2で検討するルール候補:

ルール閾値(参考)目的
全体レートリミット(blanket)2,000 req / 5分 / IP一般的なDDoS/フラッド防御
エンドポイント別(スコープダウン)予約/案件作成系: 10 req/1分 等特定URIへのブルートフォース防御

参考: AWS公式ブログ: The three most important AWS WAF rate-based rules

スコープダウンステートメントによるエンドポイント別制限

Rate-basedルールにスコープダウンステートメントを組み合わせることで、特定のURIパスにのみ厳しいレート制限をかけられる。全体のblanketルールより厳しい閾値を設定できるため、誤検知リスクを抑えつつ重要なエンドポイントを保護できる。

適用候補:

  • 予約作成API(秒間1回も叩かれることがない → 10 req/1分 程度で十分)
  • 案件作成API(同上)
  • ログイン/認証系API
  • 認証なしのエンドポイント(スクリプトで無限に叩けるリスクがあるため、特に厳しい制限が必要)

設定例:

Rate-based ルール(例: backend.fastdoctor.jp の WAF Web ACL):
  閾値: 10 req / 1分
  スコープダウン: 案件作成等の特定エンドポイントにマッチするリクエストのみ対象
  → 対象パス以外にはこのルールは適用されない

実例: staging環境で案件作成APIがスクリプトにより無限ループで叩かれ、障害が発生した。 このようなエンドポイントは秒間1回も叩かれることがない正常系のため、10 req/1分 程度の閾値で十分。

: スコープダウンの対象パスは、レートリミット導入時に各サービスのエンドポイントを精査して決定する。

別案: WAF Rate-basedルールの誤検知リスクが許容できない場合、Lambdaで一定期間に閾値以上のリクエストがあったIPを検知し、WAFのIP Blacklistに自動追加する方式も検討する。この場合、閾値やブロック期間を柔軟に制御でき、誤検知時の解除も容易になる。


Geo制限

Geo制限は導入しない。 海外勤務の医師や渡航中の患者向けサービスがあるため、国外アクセスを一律にブロックできない。


Rate Limit の設計

例外 IP

Rate-based ルール導入時に、以下の IP は Rate Limit の対象外(force-allow-ip-list)とする:

IP理由
NAT IP(各 VPC)サービス間通信が同一 IP で集約されるため、Rate Limit に引っかかるリスクがある
オフィス IP社内スタッフのアクセスが集約される
Datadog Synthetics外形監視のリクエストがブロックされないようにする

Rate Limit 導入時の注意

Rate-based ルールは Count モードで様子を見ながら 段階的に適用する。例外 IP を事前に force-allow-ip-list に登録してから導入すること。


Phase移行計画

Phase 1: WAF導入(Count)

WAF(REGIONAL)を Count モードで ALB にアタッチする。DNS 切替は不要で、既存の通信に影響を与えない。API Gateway サービスは別途検討。

WAFルール(全てCountモード、IP/UA BlacklistのみBlock):

  • IP/UA Blacklist — Block(共有リスト)
  • Amazon IP Reputation List — Count
  • Anonymous IP List — Count
  • Known Bad Inputs — Count
  • Core Rule Set (CRS) — Count
  • SQLi Rule Set — Count

この期間にやること:

  • Datadogで各ルールのマッチ数・パターンを監視
  • 誤検知パターンの収集(特にCRS、SQLi)
  • SizeRestrictions_BODY(ボディサイズ超過)、CrossSiteScripting_BODY(XSS誤検知)等のマッチを確認
  • サービスごとのアクセス元パターンを確認し、アクセス要件を整理

: ALB + WAF(REGIONAL)構成では、Phase 1.5(ALB SG 制限)は不要。WAF が ALB に直接統合されるため、バイパスが構造的に不可能(検証済み・SA 確認済み)。

Phase 2: Count → Block化(段階的)

AWS推奨の「誤検知リスクが低いルールから順にBlock化」に従い、以下の順序で切り替える。

順序ルールBlock化の条件状況
1Amazon IP Reputation ListCount運用で誤検知ゼロを確認継続観察中
2Anonymous IP List同上。VPN経由の正規アクセスがないことを確認継続観察中(VPN 経由スタッフの可能性あり)
3Known Bad Inputs同上。誤検知率は極めて低いBlock 昇格済み(fd-system staging、2026-06-23)。直近5日間の分析で14件すべてスキャナー(.env ファイルスキャン等)、正規通信への影響なし
4SQLi Rule Set検索API等でのマッチパターンを確認し、必要なら除外設定継続観察中
5Core Rule Set (CRS)最も誤検知が多い。RuleActionOverride/Label-based除外を設定してからSizeRestrictions_BODY / SizeRestrictions_QueryString を除外済み。他ルールは継続観察中

重要: 各ルールのBlock切り替え後、最低2-3日は様子を見てから次に進む。 切り替えは1ルールグループずつ行い、問題があればすぐにCountに戻せるようにする。

Block数のモニタリングアラート: Block化後の誤検知に早期に気付くため、Block数の急増を検知するDatadogアラートを設定する。LP向けWAFの運用設計書(WAF 運用設計書)で定義済みのアラート設定を参考にする。

移行条件: Phase 1でトラフィックパターンが把握できていること

Phase 3: 追加ルール・最適化

Phase 2でBlock化が安定した後、必要に応じて以下を検討・導入する。

  • Rate-basedルールの導入検討(Phase 1-2のトラフィックパターン観察結果に基づき判断)
  • BE系はLabel-based除外パターンの適用
  • 脅威IP向け厳格Rate-based(IP Reputation ラベル + scope-down)の導入検討
  • CloudFront のキャッシュ導入検討 — パフォーマンス向上やコスト削減が必要な場合に検討する。ただし CloudFront 固有の制約(GET + body → 403 等)があるため、導入前に staging で十分に検証すること
  • 移行条件: Phase 2の全ルールがBlockで安定運用されていること

WAF導入手順(概要)

詳細な作業手順・タイミング・サービスごとの導入順序は、別途「切り替え計画書」を策定しそちらに記載する。 本セクションは概要のみ記載。

ALB + WAF(REGIONAL)構成では DNS 切替は不要。WAF を ALB にアタッチするだけで導入完了。

導入フロー(概要)

  1. WAF Web ACL を作成(Terraform apply)
  2. ALB にアタッチ
  3. 動作確認(ヘルスチェック、基本的なページ表示)
  4. Datadogでモニタリング開始

切り戻し手順(概要)

問題発生時:

  1. WAF Web ACL を ALB から detach(Terraform で waf_web_acl_arn を削除して apply)
  2. 即座に WAF が無効化され、元の ALB 直接アクセスに戻る

モニタリング設計

WAFログの利用目的

  • 誤検知(False Positive)の調査: 正規ユーザーがブロックされていないか確認
  • ブロック漏れの検出: 攻撃パターンがすり抜けていないか検証
  • 緊急時の攻撃元特定: DDoS攻撃等のインシデント対応時にIPアドレス・パターンを即座に特定
  • 主に直近(数時間〜数日)のログ分析で運用。過去に大きく遡る分析は基本的に不要

監視プラットフォーム

  • Datadog で一元管理(メトリクス + ログの統合監視)
  • アラート発火 → 同一画面でログ確認 → 原因特定のワークフロー

疎通検証期間のDatadogアカウント分離:

アクセス数が多いサービスのみ、疎通検証期間(約1週間)にインフラDatadogアカウントでログ量を計測する。 それ以外のサービスは最初から本番Datadogアカウントに出力する。

サービス24h アクセス数(参考値)Datadogアカウント
backend.fastdoctor.jp3.07k(訪問者)/ 113k(APIリクエスト)インフラで検証後に本番切替
p.fstdr.jp2.08kインフラで検証後に本番切替
mental-reserve-patient.fstdr.jp1.58k最初から本番
mental-reserve-admin.fstdr.jp848最初から本番
fd-video.fstdr.jp387最初から本番
u.fstdr.jp216最初から本番
cp.fastdoctor.jp327最初から本番
その他少量最初から本番
  • インフラアカウントは現状ログが無いため、WAFログの影響を正確に計測できる
  • 検証期間中もインフラDatadogアカウントでダッシュボードでモニタリングする
  • 疎通検証完了後、本番Datadogアカウントに切り替え。ダッシュボードもexport/importする

: 上記アクセス数はCloudFlare Proxy OFF後(2026-05-21障害後)の24時間データのため、 通常時より60-70%減少している。通常時はさらに多い前提でログ量を見積もること。

ログ基盤

WAF ログ基盤は既存の LP 向け WAF と同じ構成。WAF スコープが REGIONAL に変わるが、ログの転送先・形式は同一。

ログの出力先:

  • ALLOW: WAFログには出力しない(WAF Logging Filterで DROP)
  • BLOCK/COUNT: WAF Logging Filter → Firehose → Datadog + S3バックアップ
ログタイプ保存先保存期間用途
WAF BLOCK/COUNTKinesis Firehose → Datadog15日リアルタイム監視、誤検知分析
WAF BLOCK/COUNTKinesis Firehose → S3 Backup1年(365日)監査要件、過去データ分析
CloudFront 4xx/5xxCloudWatch デフォルトメトリクス → Datadog-エラー率監視

ログ基盤の設定詳細:

項目設定
WAF Logging FilterBlock/Count/Excluded_as_Count のみ記録(Allow はDROP)
リダクションAuthorization, Cookie ヘッダーをマスク
Firehose バッファ4 MB / 60秒
S3暗号化(AES256)・バージョニング有効・パブリックアクセス全ブロック・365日保持
Datadog送信先aws-kinesis-http-intake.logs.ap1.datadoghq.com
Datadog保持期間15日

Datadog Logs Pipeline(LP設計書で構築済みをそのまま利用)

LP向け設計書で構築済みの Datadog Logs Pipeline をそのまま利用する。 FDサービス系WAFも同じ source:waf でログが送信されるため、追加のPipeline構築は不要

既存Pipeline構成:

親パイプライン: AWS Web Application Firewall(Datadog標準)
  ├─ 標準の正規化処理(action → @system.action、clientIp → @network.client.ip 等)

  ├─ ネスト1: Add waf count tag(COUNT用)
  │   └─ nonTerminatingMatchingRules からルールIDを抽出 → waf_rule タグ付与

  └─ ネスト2: Add waf block tag(BLOCK用)
      └─ terminatingRuleId からルールIDを抽出 → waf_rule タグ付与

Pipelineで付与されるタグ:

タグ内容用途
waf_actionWAFのアクション(BLOCK, COUNTアクション別集計・アラート
waf_ruleマッチしたルールID(例: core-rule-set, ip-reputationルール別集計・フィルタリング
waf_hostリクエスト先ドメイン(例: p.fstdr.jp, backend.fastdoctor.jpサービス別集計
waf_useragentリクエストのUser-AgentBot分析・不審UA検知
  • COUNT/BLOCK分離: ログ構造が異なるため(COUNTは nonTerminatingMatchingRules 配列、BLOCKは terminatingRuleId)、ネストパイプラインで分離処理
  • FDサービス系WAFのログも同じ source:waf で送信されるため、waf_host タグでLP向けWAFとFDサービス系WAFのログを区別できる

共有IPセット

共有 IP セットは REGIONAL スコープ で新規作成する(LP 向け WAF の CLOUDFRONT スコープとは別リソース)。IP リストの内容は同一のものを管理する。

共有リソーススコープ用途共有範囲
shared_ip_blacklist_ipv4REGIONAL攻撃元IP・悪意あるASレンジのブロックFDサービス WAF 全ACL
shared_ip_blacklist_ipv6REGIONAL同上(IPv6)同上
ua_blacklist(リスト)-sqlmap, nikto, nmap, masscan のブロック同上
  • IP Blacklist に追加する際は、Terraform変数定義にコメントで追加理由・追加日・対象の説明を記載する(LP設計書の運用ルールに準拠)
  • LP 向け WAF(CLOUDFRONT スコープ)とは別リソースだが、IP リストの内容は Terraform 変数で共通化する

ログコスト最適化(#11672

Geo制限は導入しないため、当初想定していた国外Blockログによるコスト圧迫リスクは低下した。ただし Block/Count ログ量が想定を超える場合は以下を検討する。

  • Datadog Index Exclusion Filter で不要なログをインデックスから除外(インデックスコスト削減)
  • 除外されたログの調査が必要な場合は S3 + Athena で全フィールドをSQL検索して対応

詳細は FDサービス系WAF 運用設計書 > ログコスト最適化 を参照。

Datadogアラート(既存 datadog/service_modules/waf/monitoring 活用)

既存のDatadog WAFモニタリングモジュールを活用し、以下のアラートを設定する:

モニター閾値目的
WAF Count by Rule300(10分間)マネージドルールのマッチ急増検知(Count中の攻撃検出)
WAF Count by IP300(10分間)特定IPからの集中アクセス検知
WAF Count by UA300(10分間)不審なUser-Agent検知

Phase 1 で重点監視すべき項目

監視対象確認ポイント対応
CRS マッチ率正規リクエストがマッチしていないか誤検知なら RuleActionOverride で個別ルールを除外
SQLi マッチ率検索API等で誤検知が出ていないか同上
Datadogログ量インジェスト量が想定内か想定以上なら WAF Logging Filter の調整を検討

マネージドルール更新の監視

AWSはマネージドルールのデフォルトバージョンを随時更新する。更新により新たな誤検知が発生する可能性がある。

  • 対策: AWS SNS通知を購読し、ルール更新時にアラートを受け取る
  • 代替案: 特定のスタティックバージョンにピン留めし、手動でアップグレード+テスト
  • 推奨: 初期はデフォルトバージョン(自動更新)で運用し、誤検知が多発するようなら固定に切り替え

ALB + WAF 構成の注意点

#注意点詳細対応
1WAF変更の伝播遅延WAFルール変更は反映まで数秒〜数分かかるルール変更はトラフィックの少ない時間帯に実施
2WCU上限REGIONAL Web ACLの上限は1,500 WCU。現設計で1,182 WCU使用、残り318 WCUルール追加時はWCU予算を確認
3マネージドルールの自動更新AWSがデフォルトバージョンを更新すると新たな誤検知が発生する可能性SNS通知を購読 or バージョン固定
4共有IPセットのスコープLP向けWAF(CLOUDFRONT)とはスコープが異なるため、REGIONAL スコープで別途作成が必要Terraform変数でIPリストを共通化し、両スコープに適用

コスト見積もり

サービスあたりの月額コスト

リソース費用備考
WAF Web ACL$5/月1 ACLあたり
WAF ルール$10/月10ルール × $1(Anonymous IP List, SQLi, サービス個別IPセット追加分含む)
WAF リクエスト$0.60/100万リクエスト
Kinesis Firehoseトラフィック依存WAFログ転送
S3 (WAFログバックアップ)微量Block/Countログのみ保存
合計(固定費)約$16/月/サービストラフィック費用除く。CloudFront 料金なし

全体コスト概算(10サービス想定)

項目月額
WAF固定費(ACL + ルール)$160
WAFリクエスト費トラフィック依存
Kinesis Firehose + S3トラフィック依存
Datadog WAFログ取り込み要確認(ログ量次第)
CloudFlare$0(DNS onlyは無料)

Datadogログコストについて: Block/Countログのみ転送するフィルタが設定済み。 Geo制限は導入しないため、国外Blockログによるコスト圧迫リスクは低い。

: CloudFront 料金が不要になるため、CloudFront + WAF 構成と比較して月 $10〜20 程度の削減が見込める


実装計画

優先順位

パイロットサービスで導入検証し、順次展開する。

fd-prod アカウント(*.fstdr.jp:

  1. パイロット(1サービス): トラフィックが少なく影響範囲の小さいサービスから開始(選定は別途)
  2. 残りの *.fstdr.jp サービス: パイロットの結果を踏まえて順次導入

fd-sys アカウント(*.fastdoctor.jp + member.fstdr.jp:

  1. *.fastdoctor.jp サービス: fd-prodでの導入が安定してから。Terraform管理場所(fastdoctor-manager/)とACM証明書が異なる

Terraform実装方針

  • 既存の template_modules/options/cloudfront-waf/ をベースに、REGIONAL スコープ版の WAF モジュールを作成
    • 追加が必要なルール: Anonymous IP List、SQLi Rule Set、サービス個別IPセット
    • 既存のルール構成(rules.tf)に追加ルールを動的に組み込む
    • REGIONAL スコープの共有 IP セット(waf-shared-sets)を新規作成
  • waf-shared-sets モジュールは既存のまま共有リソースとして活用
  • Datadog WAFモニタリング(datadog/service_modules/waf/monitoring)も既存モジュールを活用

考慮事項

項目対応方針
fd-video(WebSocket/WebRTC)WebRTCはUDP直接通信。HTTPS signaling部分のALBにWAFをアタッチ
API Gateway サービス(payment, dr-site, member)別途検討。HTTP API は WAF 非サポート。CloudFront を前段に置く構成等を検討する。詳細は「API Gateway サービスの扱い」を参照
u.fstdr.jp(Heroku リダイレクト)Heroku側にIP制限がなければ問題なし
Geo制限導入しない。海外勤務の医師・渡航中の患者向けサービスがあるため
Rate Limit 例外IPNAT IP(各VPC)、オフィスIP、Datadog Synthetics を force-allow-ip-list に登録
マネージドルール更新通知AWS SNS トピックの購読設定を検討

変更履歴

日付バージョン変更内容担当
2026-05-251.0初版作成大賀
2026-06-082.0CloudFront + WAF → ALB + WAF(REGIONAL)に方針変更。staging 検証で CloudFront 固有の制約(GET + body → 403 等)が判明したため大賀
2026-06-182.1CRS SizeRestrictions_BODY 除外方針を追記。fd-system production の WAF ログ分析で正常な業務操作(PATCH 13K〜22K バイト)が Count 検知の 82.6% を占めていたため大賀
2026-06-232.2Phase 2 実績を追記。known-bad-inputs を Block 昇格(fd-system staging)。CRS SizeRestrictions_QueryString 除外を追加(医師スケジュール管理等で検知)大賀
2026-06-232.3cp.fastdoctor.jp / u.fastdoctor.jp の WAF 分析結果を追記。SizeRestrictions_BODY の Exclusion Filter 対象に cp.fastdoctor.jp を追加。サービス一覧の備考を充実大賀
2026-07-012.4p.fstdr.jp(online-patient-mypage)の SizeRestrictions_BODY 除外方針を追記。画像アップロード(AI triage添付 + カルテ画像)で5日間659件の誤検知、全て正規医療業務大賀