先日、AWS Startupブログのインタビューで、QueryLiftのインフラにおけるセキュリティの取り組みを紹介いただきました。

私たちは、SaaSを提供する企業として、皆さまに安心してQueryLiftをご利用いただけるよう、サービスを支えるインフラのセキュリティ対策に取り組んでいます。

本記事では、インタビューでお話しした内容を掘り下げ、アクセスの制御、分析データの保護、運用中の監視といった取り組みを、設計の背景とともにご紹介します。

1. 公開経路でのリクエスト制御と内部APIの分離

CloudFrontに関連付けたWAFでリクエストを制御する

QueryLiftでは、プロダクトの配信に利用するCloudFrontにAWS WAFのWeb ACLを関連付けています。一般的なWeb攻撃や不審な送信元などに対応するAWSのマネージドルールに加え、アプリケーション層のDDoS対策向けルールも設定し、リクエストを検査・制御する構成です。

一方、マネージドルールの制限をそのまま適用すると、プロダクトで必要な操作まで拒否する場合があります。例えば、アイコンのアップロードでは、リクエスト本文のサイズ制限に例外が必要です。そこで、Common Rule SetのSizeRestrictions_BODYはCountに変更し、検出時に付くラベルを後続の独自ルールで評価します。例外の条件は、PUTメソッド、プロダクトIDを含むアイコン用パス、multipart/form-dataのContent-Typeの3つです。すべてがそろう場合だけ、このサイズ制限によるブロックから除外します。

さらに、同じアップロード経路にはIP単位のレート制限を設定しています。サイズ制限を全体で外すのではなく、機能に必要な条件だけを例外とし、その経路への過剰なリクエストにも別の制限を設けています。正常な操作を受け付けるための例外と、不正な利用を抑える制御を組み合わせた設計です。

内部ALBによるバックエンドAPIの非公開化

バックエンドAPIをインターネットへ直接公開すると、画面の入口だけでなく、API自体も外部から直接アクセスできる状態になります。QueryLiftでは、フロントエンド側の公開ALBと、バックエンド側の内部ALBを分け、バックエンドAPIを直接公開しない構成にしています。

ブラウザからのリクエストは、CloudFrontと公開ALBを経由して、フロントエンドのアプリサーバーへ届きます。このサーバーがBFF(Backend for Frontend)として内部ALB経由でWebバックエンドを呼び出します。アプリサーバーとWebバックエンドのECSタスクはいずれもプライベートサブネットに配置し、パブリックIPを割り当てていません。

この構成により、WebバックエンドのALBとECSタスクがインターネットから直接リクエストを受ける経路をなくし、外部へ公開する範囲を限定しています。これは、APIの認証・認可とは別に、ネットワーク上の公開範囲を制御する対策です。例えばダッシュボードの分析データ取得では、アプリサーバーがログイン中の利用者と対象プロダクトの情報を取得したうえで、内部APIを呼び出します。

利用者ごとに異なる応答を共有キャッシュに保存しない

分析画面の応答は、利用者や選択中のプロダクトによって内容が変わります。こうした応答を共有キャッシュに保存すると、キャッシュキーの設計によっては、ある利用者向けの分析データが別の利用者へ返されるリスクがあります。QueryLiftでは、CloudFrontからアプリサーバーへ送る動的なリクエストの経路に CachingDisabled を指定しています。一方、S3から配信する /contents/* は別の経路とし、静的コンテンツ向けのキャッシュポリシーを設定しています。

# CloudFrontの動的リクエスト向け設定から抜粋
default_cache_behavior {
  target_origin_id = "${var.env}-${var.product_name}-${var.module_name}-alb"
  viewer_protocol_policy = "redirect-to-https"
  # AWS managed policy: CachingDisabled
  cache_policy_id = "4135ea2d-6df8-44a3-9df3-4b5a84be39ad"
}

CachingDisabledは、TTLを0にしてCloudFrontのキャッシュを無効にするAWSのマネージドポリシーです。また、ダッシュボードの分析データを取得するサーバー側のfetchにも cache: "no-store" を指定しています。配信経路とアプリケーションの双方で、利用者ごとに変わる応答の扱いを明示しています。

このように、静的コンテンツの配信と利用者ごとに変わる応答を分け、分析データが共有キャッシュから誤って再利用されるリスクを抑えています。

2. 分析結果を保存する際の条件をポリシーで強制する

デフォルト暗号化と書き込み条件を組み合わせる

QueryLiftのプロダクトでは、回答分析の結果を保存するS3バケットに、専用のKMSキーを使った暗号化を設定しています。この保存先では、デフォルト暗号化の設定に加え、バケットポリシーにも書き込み条件を定義しています。

具体的には、次の操作を明示的な Deny の対象にしています。

対象

ポリシーで拒否する条件

バケット・オブジェクトへのアクセス

TLSを使用していない

オブジェクトの書き込み

暗号化ヘッダーが指定されていない

オブジェクトの書き込み

指定された方式が aws:kms ではない

オブジェクトの書き込み

指定されたKMSキーが、この保存先のキーではない

例えば、暗号化ヘッダーがない書き込みは、次のstatementで拒否します。

# aws_s3_bucket_policyのStatementから抜粋
{
  Sid       = "DenyUnencryptedObjectUploads"
  Effect    = "Deny"
  Principal = "*"
  Action    = "s3:PutObject"
  Resource  = "${aws_s3_bucket.answer_analysis_artifacts.arn}/*"
  Condition = {
    Null = {
      "s3:x-amz-server-side-encryption" = "true"
    }
  }
}

S3のデフォルト暗号化が有効でも、このポリシーがある場合、書き込み側は暗号化方式とキーを明示する必要があります。分析処理の変更時に指定が抜けたり、別のキーを指定したりしても、そのまま保存されずに拒否されます。暗号化の条件を、実装者の注意だけに頼らず守るための制御です。

Block Public Accessの4項目も有効化し、バケットやオブジェクトを直接公開する設定を制限しています。KMSキーには自動ローテーション、S3にはバージョニングを設定しています。これらは、回答分析結果の保存先を対象にした設定です。

分析処理とAPIで、必要な操作を分ける

保存先へのアクセス権限は、処理ごとにIAMロールへ付与しています。

回答分析タスクには、対象のプレフィックス配下に対する読み書きと、対象KMSキーの利用を許可しています。バケット内の一覧取得にもプレフィックス条件を設けています。

一方、Webバックエンドに付与する分析結果向けの権限は、対象プレフィックス配下の s3:GetObjectVersion と、対象キーの kms:Decrypt です。分析結果を書き込む処理と、保存されたバージョンを読み取る処理で、操作権限を分けています。

利用者にとっては、回答分析結果が公開ストレージとして扱われたり、想定外の暗号化条件で保存されたりするリスクを抑えることにつながります。また、結果を作成する処理と読み出すAPIで権限を分け、閲覧のための処理に分析結果の書き換え権限を持たせない構成にしています。これらの保存条件と操作権限はTerraformで管理し、変更時に差分を確認できます。

3. 設定の検査、脅威検出、通知と調査

構成の変更と外部へのアクセス許可を確認する

アクセス経路や保存先を制御していても、運用中の設定変更で意図しないアクセス許可が生じる可能性があります。AWS Configでは、リソースの構成と変更履歴を記録しています。Security Hubでは、AWS Foundational Security Best PracticesとCIS AWS Foundations Benchmarkの基準に沿ったチェックを有効にしています。構成の記録と基準に基づく検査を組み合わせ、設定上の問題を把握するための仕組みです。

IAM Access Analyzerでは、AWSアカウントを信頼の境界として、対象リソースのポリシーが外部へのアクセスを許可していないかを検出します。外部へのアクセス許可が見つかった場合は、意図した設定かどうかを確認するきっかけにします。

検出結果を集約し、条件に応じてSlackへ通知する

稼働中の脅威については、GuardDutyでRDSのログインイベントやFargateの実行環境を監視し、InspectorでECRイメージなどの脆弱性を検出する構成です。これらの検出結果をSecurity Hubへ集約する経路と、Slackへ通知する経路を分けています。

EventBridgeでは、Security Hubを経由するイベントに加え、GuardDutyやInspectorなどからのFindingイベントも受け取り、通知条件を評価します。GuardDutyはseverityが4以上、InspectorはCRITICALかつACTIVEの検出結果を通知対象にしています。Security Hubは、CRITICALで、かつComplianceがFAILED、WorkflowがNEWの検出結果を対象にしています。Access AnalyzerのACTIVEな検出結果も通知対象です。

通知条件に一致したイベントは、SNSからLambdaへ渡します。Lambdaでは、重要度、対象リソース、検出内容とAWSコンソールへのリンクを整形し、Slackへ通知します。検出結果の集約先と日常の通知先を分け、対象リソースの詳細確認へ進めるようにしています。

検出後の調査に必要なログを残す

通知だけでは、どのようなリクエストや通信が発生したかを把握できません。そのため、WAFのリクエストログ、CloudFrontとALBのアクセスログ、VPC Flow LogsをS3へ記録する設定も管理しています。WAFでの判定や配信・接続状況、ネットワーク通信を、それぞれのログから調べられる構成です。

入口での制御や保存条件の強制に加え、設定上の問題や稼働中の脅威を検出し、ログを使った調査につなげます。異なる層の情報を残すことで、問題が疑われたときに原因と影響範囲を確認するための材料を確保しています。

おわりに

今回は、QueryLiftのプロダクトで分析画面を利用するときのアクセス経路、回答分析結果の保存条件と権限、運営側への検知・通知を紹介しました。利用者が確認する分析データの処理や保管、異常の兆候の把握について、具体的な設計を積み重ねています。こうした取り組みを通じて、安心して利用いただけるサービスを目指しています。

QueryLiftでは、生成AI上で企業がどのように語られているかを継続的に観測することを、GEO for Cognitive Securityの取り組みの一つとして位置づけています。こうした分析を提供するサービスでは、分析結果の保管やアクセスの管理も必要です。本記事で紹介した設計は、こうした分析を支える基盤の取り組みです。認知汚染のリスクと監視の考え方については、「認知汚染とそのセキュリティとしてのGEO/LLMO」で紹介しています。

分析の高速性と正確性については、「分析の高速性と正確性を支えるQueryLiftのデータ設計の取り組み」で紹介しています。GEO for Cognitive Securityのサービスについては、サービス紹介ページをご覧ください。

QueryLiftの事業とAWS基盤の全体像については、AWS Startupブログの記事もご覧ください。