コンテンツにスキップ
ペンテストの仕組みと設計

ペンテストの仕組みと設計

AWS Security Agent
検証日: 2026-10-18 / リージョン: ap-northeast-1 / 対象: AWS Security Agent(now part of AWS Continuum)

はじめに

AWS Security Agent の仕様を調べるために実機で検証した。本記事は、その前提となる仕組みの解説である。Security Agent のペネトレーションテスト(以下、ペンテスト)機能について、実行の流れと料金、設定を構成するリソース、そして実環境に近い「インターネット公開 + AWS WAF」の構成で試験するための設計をまとめる。

検証では、意図的に脆弱性を仕込んだ自作の Flask アプリ(以下、対象アプリ)を試験の対象にした。対象アプリのドメイン名は、筆者の保有ドメインの配下に委任したサブドメイン(lab.infra.jp)の下の pentest.lab.infra.jp で、本文にもそのまま書く。対象読者は、AWS での構築経験があり(VPC、ALB、IAM、Terraform を扱える程度)、脆弱性診断には詳しくない人を想定している。

AWS Security Agent とは

AWS が 2025 年 12 月にプレビュー公開し、2026 年に一般提供した AI エージェント型のアプリケーションセキュリティサービス。動いている Web アプリに対して AI が攻撃を試みて脆弱性を実証するペンテストを中心に、コードや設計書のレビュー、脅威モデリングの機能を持つ(機能の一覧は後述)。

2026 年秋時点ではブランドが「AWS Continuum」に統合され、公式ページでは「AWS Security Agent (now part of AWS Continuum)」と表記される。ただし、コンソールの URL、API、CLI のコマンド(aws securityagent ...)の名前は旧称の securityagent のまま変わっていない。料金ページだけは「AWS Continuum for penetration testing」と表記され、Security Agent の名前が出てこない。本記事では、なじみのある「Security Agent」と呼ぶ。

このページの位置づけ

Security Agent のペンテストには、公開されたアプリに対して AWS 側から攻撃を試みる「公開方式」と、利用者の VPC の中にテストを実行する AI(以下、エージェント)を置いて閉域で行う「Private VPC 方式」(以下、閉域方式)がある。本記事は公開方式を対象とする。閉域方式は本記事では扱わない。

実環境のアプリは Application Load Balancer(以下、ALB)や CloudFront の前段に AWS WAF(以下、WAF)を置いていることが多く、公開方式ではエージェントのリクエストも WAF の検査を受ける。本記事では、この構成で次の 2 つを両立させる設計を説明する。

  • エージェントの通信を、WAF の影響を受けずにアプリへ届けること
  • エージェント以外の第三者を、アプリに届かせないこと

さらに、同じ構成に AWS のマネージドルールを加え、評価順を入れ替えて「WAF でどれだけ防げるか」を測れるようにする。

対象アプリは、応答する URL のパスが 8 つしかない小さな Web アプリである。当初は練習用の脆弱アプリとして有名な OWASP Juice Shop を使う予定だったが、Classmethod の検証記事では 55 分で手動停止しても、ペンテストの課金単位である Task-hour(後述)を 3.66 消費しており、金額にして約 $183 相当だった(同記事では無料トライアルが適用され、請求はなかった)。無料トライアルが適用されない可能性を考えると、できるだけ小規模なアプリにした方が Task-hour の消費、つまり料金を抑えられると考えた。中身を自分で把握できるアプリの方が検出結果を評価しやすいという観点もある。仕込む脆弱性の種類は、AWS 公式デモ(sample-security-agent-demo)の Flask アプリの一覧を参考にした。仕込んだ 7 種類の一覧は、リポジトリの docs/planted-vulnerabilities.md に PLANTED-1〜7 として記載している。

この検証の記事は 4 本に分かれている。

記事内容
本記事ペンテストの仕組み、料金、公開方式の設計
検証結果3 回の実行とコードレビューの結果、Task-hour の実測、想定外の発見
ペンテストの手順Terraform による構築から実行、クリーンアップまで
GitHub 連携とコードレビューの手順(後日公開)GitHub App の連携とコードセキュリティレビュー

AWS Security Agent の 4 つの機能

Security Agent には機能が 4 つある。

機能内容本記事での扱い
ペンテスト動いているアプリに対し、AI エージェントが攻撃を試みて脆弱性を実証する主題
コードセキュリティレビューリポジトリ全体や Pull Request を解析し、問題箇所を指摘する料金の位置づけのみ。手順は GitHub 連携とコードレビューの手順(後日公開)、ペンテストとの比較は検証結果
脅威モデリング設計書やコードから、想定される攻撃(脅威)の一覧を作る。分類には STRIDE(なりすまし・改ざん・否認・情報漏えい・サービス妨害・権限昇格の頭文字)を使う対象外
設計セキュリティレビュー設計書を組織のセキュリティ要件と照合する対象外

ペンテストの流れと料金

実行の 4 フェーズ

1 回の実行(Run)は 4 つのフェーズで進む。Web アプリの実行画面では、プリフライト → 静的分析 → ペネトレーションテスト → ファイナライズ中 と表示される。

フェーズ内容
Preflight実行前の事前確認。ログ基盤の準備とターゲットへの到達性確認を行い、ここで失敗すると Task-hour を消費せずに止まる。「Logging Infrastructure Setup」「Endpoint Accessibility Validation」「Setup Testing Environment」の 3 つのチェックが順に行われる
Static analysis決まった手順で対象を一通り調べ、見つかった疑いを 1 件ずつ確かめる。ソースコードを渡していない場合もこの段階は動き、Task-hour を消費する
Pentest複数の AI エージェントが並列にリスク種別ごとの検査を行う。再現できる証跡が揃った脆弱性だけを検出結果として確定させる
Finalizing検出結果の最終確認と取りまとめ。この段階でも Task-hour を消費する(実測は検証結果を参照)。PDF のレポートは、実行後に画面のボタンで別途生成する

Security Agent は確定した脆弱性 1 件ごとに記録を作る(英語の画面や API では Finding。以下、検出結果)。

静的分析とペネトレーションテストは、どちらもアプリに実際にリクエストを送る。ソースコードを渡していなければ、静的分析はコードを読む解析ではない。違いは検査の組み立て方にある。

  • 静的分析: クローラーやポートスキャン、TLS の検査、汎用のスキャナーといった決まった道具で対象を一通り調べる。検証では、1 回のリクエストで反応が分かる種類の脆弱性がこの段階で見つかった
  • ペネトレーションテスト: リスクの種類ごとにエージェントが起動し、ログインして状態を作るなど、複数の手順をつないで試す。検証では、ログインや他人の ID を使う種類の脆弱性がこの段階で見つかった

ペネトレーションテストの段階では、対象アプリに該当する機能がない種類(サーバーサイドテンプレートインジェクション、コマンドインジェクション、ファイルアップロード、SSRF、JWT、XXE など)も網羅的に試される。見つからずに終わっても、その分の Task-hour は消費する。

料金の単位: Task-hour

料金は Task-hour という単位で課金され、2026 年 10 月時点の単価は $50 / Task-hour、計算は秒単位である。Task-hour は「並列に動く全タスクの稼働時間の合計」で、実行の開始から終了までの経過時間とは別物である。4 つのタスクが並列に動けば、経過時間 1 時間で約 4 Task-hour になる。Web アプリの画面では「タスク時間」と表示される。

  • 無料トライアル: 新規顧客には 2 か月・各月 400 Task-hour までの無料トライアルがあり、最初のペンテスト実行で開始する。400 Task-hour は単価 $50 で $20,000 相当、2 か月で $40,000 相当に当たる
  • 他の 3 機能の扱い: 無料トライアルの対象はペンテストで、他の 3 機能(コードレビュー・脅威モデリング・設計レビュー)はプレビュー中はもともと無料。ただし利用枠があり、アカウントごとに設計レビューは月 200 件、コードレビューは月 1,000 件まで。「無料」でも Task-hour は消費され、Cost Explorer にはコードレビュー専用の使用タイプで使用量が計上される(金額は 0 ドル。実測は検証結果を参照)

最大タスク時間と手動停止

1 回の実行に設定できる Task-hour 上限(最大タスク時間)の最小値は 20 時間で、定価 $1,000 相当に当たる。それより早く止めるには手動停止する。この最小値は画面の選択肢だけでなく API の制約で、CLI で 6 時間を指定すると「Max task hours must be at least 20 hours」というエラーで拒否された。

Web アプリの画面の説明には「上限を低く設定すると、テストが完了する前に実行が停止し、発見される脆弱性が減少する可能性がある」とある(選択肢はペンテストの手順の手順 6 を参照)。

実行が終わるのは、リスクの種類を順に回り切ったときか、最大タスク時間に達したときである。残りの種類の数は画面に出ないので、終わる時刻は事前に分からない。上限到達や手動停止の時点までに確定した検出結果は保持される。停止した実行は再開できない。

再検証

修正した脆弱性を 1 件ずつ試し直す「再検証」がある。ペンテスト全体をやり直さず、選んだ検出結果だけを再テストし、「Active(まだ悪用できる)」か「Resolved(直った)」を返す。1 件の検出結果につき 3 回までは無料で、それを超えると Task-hour で課金される(この検証では、脆弱性を直していないので使っていない)。

出典: 料金ページ、FAQ、Revalidate penetration test findings、Create a penetration test(Max task hours)

費用の見込みと抑え方

検証前の見込みは次のとおり。見込みと実測の対比は検証結果に記載している。

項目目安
インフラ(ALB、EC2、インターフェース型エンドポイント 3 つ、ホストゾーン、ログ)約 $2.5 / 日。当日中に destroy する前提
AWS WAFWeb ACL $5.00 / 月 + ルール $1.00 / 月 × 6 本(ヘッダー許可 1 + マネージドルール群 5。操作者の許可を加えれば 7 本)= 約 $11〜12 / 月。時間按分なので 2 日間なら約 $0.80。リクエスト課金は $0.60 / 100 万件で、ペンテスト数回分なら数セント。AWS 提供のマネージドルール群に追加料金はない(Marketplace 製は別)
Security Agent1 回の完走で 3〜6 Task-hour、金額にして $150〜300 相当と推定した。根拠は AWS 公式の最小例「新規 API のテスト」が 3.46 Task-hour であること。マネージドルール群を先に評価する構成では、WAF に遮断される分だけ増える可能性がある。無料トライアルが適用されれば $0

Security Agent の費用は次の考え方で抑えた。

  • 最初の実行の位置づけ: 「無料トライアルが適用されるかを確かめる実行」とする。完走させても推定 $150〜300 が上限で、翌日の請求で $0 なら以降の実行は枠内で回せる。請求が付いていれば、次の実行を行うかをその時点で判断する
  • 止める線の事前決定: 実行中は画面で Task-hour の消費を見て、止める線(例: 6 Task-hour)を先に決めておく。設定できる上限の最小値は 20 Task-hour なので、それより低く抑えたい場合は手動停止をするしかない
  • 月額の通知: AWS Budgets で Security Agent の月額に通知を付ける(この検証では設定せず、Cost Explorer を翌日に確認した)

Security Agent を構成するリソース

2 つの操作画面

Security Agent には操作する画面が 2 つあり、役割が分かれている。どちらも日本語で表示される。

画面利用者役割
AWS マネジメントコンソール(以下、コンソール)管理者「何をテストしてよいか」の枠組みを決める。Agent Space の作成、テスト対象ドメインの登録、使ってよい VPC や IAM ロールの指定。この検証ではこの部分を Terraform で作り、コンソールは結果の確認に使った
Security Agent の Web アプリケーション(リソース名は Application。以下、Web アプリ)テストを実行する利用者AWS 側でホストされる専用画面(利用者が構築・運用するものではない)。枠組みの中で Pentest を作成・実行し、結果(検出結果)を見る。コンソールの Agent Space 画面の「ウェブアプリ」タブにある「管理者アクセス」で開く。この検証では Pentest の作成も Terraform で行い、ドメイン検証の実行も CLI で行ったので、この画面は最大タスク時間の設定、実行、結果の確認に使った

企業では「セキュリティ部門がコンソールで枠組みを決め、開発チームが Web アプリでテストを回す」という分担を想定した作りになっている。1 人で検証する今回は両方を自分で操作した。

コンソールで Agent Space を開くと、上段に 4 機能のカードが並ぶ。ペネトレーションテスト・設計レビュー・脅威モデルは「準備完了」、コードレビューだけが「セットアップが必要」になっている。Web アプリへのサインイン方式は、Application を作るときに次の 2 つから選ぶ(公式ドキュメント)。この検証は IAM のみである(Terraform の Application リソースで Identity Center の設定 id_c_configuration を空にした状態)。

方式誰が使えるか(認証)開き方
IAM Identity Center(SSO)Agent Space に割り当てた Identity Center のユーザーWeb アプリの URL を開き、Identity Center でサインインする。AWS コンソールに入れない利用者に使わせる方式
IAM のみ(この検証)AWS コンソールにサインインできる IAM のユーザーやロールAgent Space の「ウェブアプリ」タブにある「管理者アクセス」のリンクから開く。コンソールのサインインが Web アプリの認証を兼ねる

どちらの方式でも、URL を知っているだけでは開けない。Web アプリの URL(https://app-<ID>.securityagent.global.app.aws/)に認証なしでアクセスすると HTTP 400 が返る(検証後に curl で確認)。

Agent Space sa-pub-vulnapp の詳細画面。4 機能のカードと、ウェブアプリタブの管理者アクセスボタン

Web アプリのホームの「最近のプロジェクト」に、Terraform で作った Pentest(vulnapp-public-waf)がペネトレーションテストとして載る。

Web アプリのホーム。最近のプロジェクトに vulnapp-public-waf が 1 件

リソースの階層

設定は次のリソースで管理される。

Application(アカウントに 1 つ)   … 前節の Web アプリに対応するリソース(対象アプリではない)。ほかのリソースの親ではない
Target Domain(ドメインごと)      … テストしてよいドメイン。アカウントの中に作り、Agent Space に登録する
Agent Space(対象アプリごと)      … 許可するサービスロール・Target Domain のまとまり(閉域方式では VPC も)
└─ Pentest(テスト設定ごと)       … ターゲット URL、カスタムヘッダー、認証情報の定義(閉域方式では VPC も)
   └─ Run(実行ごと)              … Pentest を実行した 1 回分。検出結果と Task-hour はここに付く

Agent Space → Pentest → Run の親子関係は API で決まっている。Pentest の作成(create-pentest)には Agent Space の ID が、実行の開始(start-pentest-job)には Agent Space と Pentest の ID が必須である。Target Domain は独立したリソースで、先に作ってドメイン検証用の TXT レコードのトークンを受け取り、Agent Space の作成時に target_domain_ids で登録する。Application も独立したリソースで、API 上はどのリソースの引数にもならない。コンソールでは「セットアップ」で最初に作り、この検証の Terraform でも Agent Space より先に作る順にしている。Application がない状態で Agent Space を作れるかどうかは確かめていない。

リソース説明Terraform リソース
Application前節「2 つの操作画面」で述べた Web アプリ、つまり利用者が Pentest を作成・実行する AWS 側の画面に対応するリソース。AWS の命名が紛らわしいが、対象アプリとは無関係。アカウントに 1 つだけ作れ、コンソールの「セットアップ」ボタンが作るものと同じ。利用者が決めるのは Web アプリへのログイン方式(IAM か IAM Identity Center か)と、後述の Application ロールだけ。この検証は 1 人で行うため IAM 専用アクセスを選んだ。Identity Center は Organizations 外の単独アカウントでも「アカウントインスタンス」として使えるが、ユーザー管理の手間が増えるだけで今回の目的には不要awscc_securityagent_application
Agent Space対象アプリ 1 つ分の設定のまとまり。このアプリのテストで使ってよい AWS リソースと、テストしてよいドメインを登録する。登録できるリソース(API では aws_resources)は、IAM ロール(サービスロール)、VPC、CloudWatch ロググループ、S3 バケット、Secrets Manager の秘密情報、Lambda 関数の 6 種類で、この検証ではサービスロール 1 つだけを登録した。ここに登録されていないものは Pentest の設定で選べないawscc_securityagent_agent_space
Target Domainテスト対象として登録するドメイン名。他人のサイトを攻撃できないよう、「自分が管理しているドメインである」ことを確認してからでないとテストできない。確認方法は 3 つ: DNS に指定された TXT レコードを置く(DNS_TXT、この検証で使った方式)、Web サーバーの決まった URL に確認用ファイルを置く(HTTP_ROUTE)、VPC の中から名前解決してプライベート IP に解決することを確かめる(PRIVATE_VPC、閉域方式で使う)awscc_securityagent_target_domain
Pentestテストの設定 1 件。ターゲット URL、付与するカスタムヘッダー、ログイン用の認証情報、閉域の場合はエージェントを置く VPC などを定義する。同じ Pentest を何度でも実行(Run)できるawscc_securityagent_pentest

Pentest の設定項目

Web アプリで Pentest を新しく作る場合、利用者はホームの「ペネトレーションテストを作成」から設定項目を入力する。この検証では Pentest を Terraform で作ったので、その入力は行っていない。代わりに、作成済みの Pentest を Web アプリで開いて「詳細の変更」を押すと、5 ステップのウィザードに Terraform で設定した内容が入った状態で表示され、設定項目の全体を確認できる。次の表はその画面で見た項目である。新規作成の画面が同じ構成かどうかは、この検証では開いていないので確かめていない。設定の定義そのものは、リポジトリの terraform/public-method/security-agent/security_agent.tf(awscc_securityagent_pentest)にある。最大タスク時間だけは Terraform の属性がなく、Web アプリか CLI で設定する。

ステップ内容
1 ペネトレーションテストの詳細テストの対象範囲(ウェブアプリケーション)とターゲット URL。「高度なネットワークアクセス」を展開すると、カスタム HTTP ヘッダーがある。説明文によれば、エージェントのリクエストには既定で User-Agent: securityagent が付く
2 VPC リソース(オプション)閉域方式で使う。公開方式では選ばない
3 認証情報(オプション)ログインが必要なアプリをテストするときの認証情報。「認証情報を追加」で、ID とパスワード(2 要素認証があるアプリでは TOTP の秘密も)を直接入力するか、Secrets Manager・Lambda・IAM ロールを指定し、認証情報ごとにログインの手順を文章で添える(詳細は後述)。この検証では 0 件(ログインなしのテスト)
4 ネットワーク設定エージェントが到達してよい範囲(ネットワークスコープ)。URL を「ターゲット(攻撃が許されている URL)」「アクセス可能(アクセスできるが攻撃してはならない URL)」「スコープ外(アクセスしてはならない URL)」に分類する。既定ではターゲット URL だけがアクセス可能で、画面の説明には、ホスト型ログインページや CDN などアプリが必要とするものは「アクセス可能」に追加するようにとある。この検証はターゲット URL 1 件だけ。「派生例」として、サブドメインなどトラフィックを送らない URL の例が表示される
5 その他の設定(オプション)サービスロール、CloudWatch ロググループ(既定は /aws/securityagent/<Agent Space 名>/<Pentest ID> を自動作成)、リソース(アプリの参照資料。ファイルのアップロード、GitHub リポジトリ、S3 の資料。画面の説明では、コンテキストを追加するとカバレッジと精度が向上する。この検証では 0 件)、継続的なペンテスト(プレビュー)、自動コード修正、検出結果のパーソナライゼーション、クリーンアップ、最大タスク時間。継続的なペンテストと自動コード修正は、リポジトリを接続していないと選べない

ウィザード手順 5 の上半分。サービスロール、CloudWatch ロググループ、リソース、継続的なペンテスト、自動コード修正、検出結果のパーソナライゼーション、クリーンアップ

IAM ロールが要る理由

ペンテストの攻撃の通信は、AWS 側のインフラからインターネット越しに対象アプリへ送られる。この通信そのものに、利用者のアカウントの IAM の権限は要らない。権限が要るのは、実行中に Security Agent が利用者のアカウントの中に作用する場面である。公開方式の本検証で実際に起きたのは次の 2 つである。

  • 実行の記録(タスクログ)を、利用者のアカウントの CloudWatch Logs のロググループ(/aws/securityagent/<Agent Space 名>/<Pentest ID>)に書く。Web アプリの「ログ」タブで見るエージェントの推論とコマンドは、このロググループから読まれている
  • Web アプリの画面で利用者が行った操作(Pentest の作成・実行、検出結果の閲覧など)を、Security Agent の API 呼び出しとして実行する

このほか、閉域方式では利用者のサブネットにネットワークインターフェース(ENI)を作る権限、ログイン付きのテストでは認証情報を置いた Secrets Manager や Lambda を読む権限が要る。公式ドキュメント(Create the IAM roles for AWS Security Agent)は、「Security Agent は利用者のアカウントの IAM ロールを引き受け、Web アプリの利用者に代わって自身の API を呼び、評価の対象として Agent Space に登録した AWS リソースに到達するために使う」と説明している。

この検証で使った 2 つのロール

コンソールで Security Agent をセットアップすると、ロールは自動で作られる。この検証は Application から Pentest までを Terraform(awscc プロバイダ)で作る方針にした。awscc のリソースにはロールの ARN を渡す項目があり(Application の role_arn、Agent Space の aws_resources.iam_roles、Pentest の service_role)、コンソールのような自動生成はない。そのため、ロールも Terraform(terraform/modules/security-agent-roles/main.tf)で定義した。

ロール(公式の呼称)引き受ける主体用途この検証での名前
Application ロールWeb アプリ利用者が Web アプリの画面で行った操作を、Security Agent の API 呼び出しとして実行する。AWS 管理ポリシー AWSSecurityAgentWebAppPolicy と、タスクログの読み取り(logs:GetLogEvents など)を許可するポリシーを付けるsa-pub-securityagent-app
Agent Space サービスロール(以下、サービスロール)実行中の Security Agent実行中に利用者のアカウントの中で行う操作のため。自分自身のロールの参照、評価ログの書き込みなど。VPC や Secrets Manager などを Agent Space に登録すると、コンソールはその分の権限をこのロールに足す。ペンテスト・コードレビュー・脅威モデリングが共用するsa-pub-securityagent-pentest

どちらも IAM ロールで、Security Agent というサービス(サービスプリンシパル securityagent.amazonaws.com)が一時的に引き受けて(AssumeRole)使う。利用者がアクセスキーを作って Security Agent に渡す場面はない。Agent Space の「IAM ロール」の欄に登録するのはサービスロールであり、Application ロールとは別物である。コンソールで作る場合、Application ロールは最初のセットアップの「Permissions」の段階で「デフォルトロールを作成」か「別のロールを使用」を選ぶ(Set up AWS Security Agent)。サービスロールは Agent Space の設定画面で「デフォルトロールを作成」か「既存のサービスロールを使用する」を選ぶ(画面はGitHub 連携とコードレビューの手順(後日公開)の手順 4 を参照)。どちらもポリシーの中身はコンソールが生成する。このほか、ペンテストのスケジュール実行を有効にしたときだけ作られる Scheduler 実行ロール(EventBridge Scheduler が引き受ける)があるが、この検証では使わない。

公式ドキュメントのロール定義との違い

公式ドキュメント(Create the IAM roles for AWS Security Agent)は、コンソールが自動生成するロールの中身(信頼ポリシーと権限ポリシーの JSON)を、監査できるように公開している。この検証の Terraform の定義は、そのページの JSON をそのまま写したものではなく、次の違いがある。

  • 権限の範囲が広い: サービスロールの iam:GetRole と CloudWatch Logs の権限のリソースが *。公式は自分自身のロールと /aws/securityagent/<Agent Space 名>* に限定している
  • 足りない権限がある: サービスロールの secretsmanager:GetSecretValue、信頼ポリシーの aws:SourceArn 条件、Application ロールの sts:SetContext(Web アプリへのサインインに IAM Identity Center を使う場合に要る。この検証は IAM 専用アクセスで、試していない)
  • タスクログの読み取りは一致する: 公式ドキュメントは、Application ロールには管理ポリシー AWSSecurityAgentWebAppPolicy とカスタマー管理ポリシーの両方が要るとし、後者の例として logs:GetLogEvents と secretsmanager:CreateSecret を許可する JSON を載せている。この検証では、Web アプリの「ログ」タブが開けないことから実測でこの不足に気づいて足した(経緯は検証結果)

ログインが必要なアプリをテストする場合は、Pentest の設定の手順 3「認証情報」で、Security Agent にログイン用の ID やパスワードなどの認証情報を渡し、認証情報ごとに「どの URL のどのフォームでログインするか」を文章で添える(ログインプロンプト)。渡し方は 4 つあり、どれを選ぶかでサービスロールに要る権限が変わる(Provide authentication credentials for penetration testing)。Secrets Manager の秘密情報、Lambda 関数、引き受ける IAM ロールは、先に Agent Space に登録しておき、Pentest の認証情報の設定ではそこに登録したものだけを選べる。コンソールで作ったサービスロールには、Agent Space に登録したリソースの種類ごとに、その権限が自動で足される。この検証で Terraform に書いたサービスロールには、4 つのどれに要る権限も入っていない。この検証はログインなしのテストしか行っていないので支障はなかったが、ログインが必要なテストにはこのままでは使えない。

認証情報の渡し方サービスロールに要る権限(公式ドキュメント)この検証の Terraform のサービスロール
Pentest の設定の手順 3「認証情報」で ID とパスワードを直接入力公式ドキュメントは保存先を明記していない。ロールの定義には、Application ロールに secretsmanager:CreateSecret、サービスロールの基本ポリシーに「Security Agent が Agent Space 用に作る秘密情報」の secretsmanager:GetSecretValue が含まれているどちらもない
Secrets Manager の秘密情報を指定secretsmanager:GetSecretValue と secretsmanager:DescribeSecretない
Lambda 関数で動的に取得lambda:InvokeFunction(関数は 30 秒以内に応答)ない
IAM ロールを引き受けてログイン(Cognito や IAM 認証のアプリ)引き受ける用のロールを別に作り、Agent Space に登録する。そのロールに認証システム(Cognito や API Gateway)への権限作っていない

上の 4 つはどれも動かしていない。本番で使うなら、コンソールの自動生成に任せるか、公式ドキュメントの JSON に合わせる。

公開方式でエージェントだけを通す設計

エージェントの識別手段

公開方式では、Security Agent は AWS 側のインフラからインターネット越しにターゲット URL へリクエストを送る。送信元 IP は公表されていないため、WAF で送信元 IP を許可する方法は使えない。公式ドキュメントが示す手段は次の 2 つである。

手段内容制御への利用
User-Agent既定で securityagent が付く誰でも偽装できるので不可
カスタムヘッダーPentest の設定で任意のヘッダー名と値を指定でき、エージェントの全リクエストに付く(Create a penetration test)。この検証では名前を X-Pentest-Token、値を Terraform が生成する 32 文字の乱数にした値を秘密にすれば、エージェントだけを識別できる

以下、このヘッダーをカスタムヘッダー、カスタムヘッダーが一致したリクエストだけを許可する WAF のルールをヘッダー許可ルールと呼ぶ。

ホスト名の公開と既定動作の遮断

HTTPS を用意した時点で、ホスト名は世界に知られる。公的な TLS 証明書は、発行されると必ず「証明書の透明性ログ(Certificate Transparency。誰でも閲覧できる発行記録の公開台帳)」に記録され、そこには証明書のホスト名(この検証の対象アプリなら、そのホスト名)が書かれる。AWS Certificate Manager(以下、ACM)が発行する公開証明書もすべて自動的に記録され、ブラウザ側の要件のため記録を止めることはできない(Certificate Transparency Logging(ACM ドキュメント))。台帳は crt.sh のような検索サイトで誰でも引ける。

この台帳を監視している攻撃者やスキャナは、DNS を誰にも教えていなくても「新しいサーバーが公開された」とすぐに知り、アクセスしてくる。したがって、WAF のルールのまとまりである Web ACL の既定動作(どのルールにも一致しないリクエストの扱い)を遮断にして第三者を通さない構成は、対象アプリを公開方式で試験するうえで欠かせない。検証中に実際に第三者が来たかどうかは検証結果に記載している。

Web ACL のルール構成

ALB に WAF の Web ACL を関連付け、次の 3 種類のルールで構成する。

ルール動作役割
ヘッダー一致X-Pentest-Token の値が秘密値と完全一致 → 許可エージェントを通す
マネージドルール群AWS が提供・更新するルールセット 5 つ(内訳は次の表)。一致 → 遮断本番相当の防御
既定動作遮断ヘッダーを持たない第三者は届かない

どちらの評価順(後述)でも、ヘッダーを持たないリクエストはアプリに届かない。対象アプリを公開方式で試験しながら、第三者には実質非公開にできるのはこの既定動作による。この状態でも、Security Agent のドメイン検証と Preflight が通り、検査が最後まで進むことを確かめた(詳細は検証結果)。

ただし、Preflight の到達性チェックは、WAF の 403 でも「HTTP の応答が返る」ことで到達可能と判定された。カスタムヘッダーの設定を誤ってもプリフライトでは気づけない、ということでもある。

マネージドルール群の選定

AWS が提供する WAF のルールセットである AWS マネージドルール(以下、マネージドルール群)は、実環境で広く使われる基本セットに、対象アプリに仕込んだ脆弱性の種類に対応するものを加えた 5 つにした。合計 1,400 WCU で、Web ACL の既定の容量上限 1,500 WCU に収まる(WCU は Web ACL の容量単位)。

ルールグループWCU選んだ理由
AWSManagedRulesCommonRuleSet700OWASP Top 10 に挙がる広い範囲の攻撃パターン(XSS、不正な入力など)を対象にする基本セット。AWS が最初に適用することを推奨している
AWSManagedRulesKnownBadInputsRuleSet200既知の脆弱性を突くことが分かっているリクエストパターン(Log4j の JNDI 文字列など)を遮断する。基本セットと対で使われることが多い
AWSManagedRulesSQLiRuleSet200SQL インジェクションのパターンを遮断する。対象アプリの /search(PLANTED-1)に対応する
AWSManagedRulesLinuxRuleSet200Linux 固有のファイル(/etc/passwd など)へのアクセスを狙うパターンを遮断する。対象アプリの /download(PLANTED-4、パストラバーサル)に対応する
AWSManagedRulesAdminProtectionRuleSet100管理画面のパス(/admin など)への外部からのアクセスを遮断する。対象アプリの /admin(PLANTED-3、PLANTED-7)に対応する

PHP や WordPress 向けのルールグループは対象アプリの技術に関係しないので入れない。Bot Control は追加料金が要るうえ、エージェントの自動アクセスをボットとして扱う可能性があり、今回の目的(WAF をすり抜ける脆弱性の観測)から外れるので入れない。この 5 つはサーバーワークスの比較検証と同じ組み合わせでもあり、結果を見比べられる。

評価順の 2 つのモード

評価順を入れ替えた 2 つのモードを Terraform の変数 waf_mode で切り替える。

モード評価順エージェントの扱い第三者の扱い
agent-first①ヘッダー一致 → 許可、②マネージドルール群WAF の検査を受けずにアプリへ届く。アプリ自体が持つ脆弱性がそのまま検出される。本番で定期的に回す形既定動作で遮断
managed-first①マネージドルール群(遮断)、②ヘッダー一致 → 許可攻撃パターンに一致したリクエストは WAF で止まる。止まらなかったものだけ届く既定動作で遮断

agent-first の構成でも、カスタムヘッダーを持たない通信には、マネージドルール群が既定動作より先に評価される。

managed-first ではヘッダー許可ルールを無効にするのではなく、順番を後ろに回す。既定動作が遮断なので、ヘッダー許可を外すとエージェントの通信がすべて落ちて試験にならない。

検証では、agent-first で 2 回(手動停止と完走)、managed-first で 1 回実行した。以下、それぞれを agent-first(手動停止)、agent-first(完走)、managed-first と呼ぶ。agent-first(完走)にあって managed-first にない検出結果が「WAF が防いだ脆弱性」、両方にある検出結果が「WAF では防げない脆弱性」である。比較の結果は検証結果に記載している。

秘密値の扱い

秘密値は Terraform(random_password)が生成し、WAF の許可ルールと Pentest のカスタムヘッダーの両方に同じ値を渡す。値は Terraform の状態ファイル(state)に平文で入るため、state はリポジトリに含めない。

秘密値は画面や CLI の出力にも現れる。Web アプリの「設定」タブと、実行後の「実行の概要」タブにある「実行範囲と制限」の欄、batch-get-pentest-jobs の出力には、値が平文で表示される。一方、実行中のタスクログに出るコマンドでは、値は [REDACTED] と伏せられる。扱いの注意点はペンテストの手順に記載している。

検証環境の構成

検証環境の構成図

ALB は HTTPS(443)のみを受け、ACM 証明書(DNS 検証)を使う。ALB のセキュリティグループはインターネットから 443 を受け、選別は WAF に任せる。対象アプリを動かす EC2 はプライベートサブネットに置き、パブリック IP を持たせず、インターネットへの経路もない(NAT なし)。ドメインは、保有ドメインの配下に委任したサブドメイン(lab.infra.jp)を Route 53 のパブリックホストゾーンで管理し、対象アプリのホスト名はその配下の pentest.lab.infra.jp にした。構成の詳細はペンテストの手順に記載している。

Terraform で作れる範囲と限界

ディレクトリの分割

この検証では、AWS 側の設定変更をすべて Terraform で行った。コードは 3 つのディレクトリ(ルートモジュール)に分かれていて、番号順に apply する。

1. terraform/public-method/dns-zone/       委任先のホストゾーンを作る
        │  (手作業) 出力されたネームサーバーをレジストラに NS レコードとして登録
        ▼
2. terraform/public-method/network-app/    VPC、EC2、ACM 証明書、ALB、WAF、ヘッダーの秘密値を作る
        │  (自動) 2 の出力(ターゲット URL、秘密値)を 3 が読む
        ▼
3. terraform/public-method/security-agent/ IAM ロール、Application、Agent Space、Target Domain、Pentest を作る

分けている理由は、1 と 2 の間に手作業(レジストラへの NS 登録)が挟まることと、2 が終わらないと 3 が作れない(Pentest がターゲット URL や秘密値を必要とする)ことの 2 点である。

Terraform で作れるリソース

Security Agent の 4 リソース(Application、Agent Space、Target Domain、Pentest)は、awscc プロバイダで作れる。4 リソースは awscc 1.78.0 で追加された。Application(アカウントに 1 つのリソース)も、destroy で削除でき、作り直しもできた。Pentest 作成については、公式サンプルに Cloud Control 経由の作成がタイムアウトする旨の注記があるが、この検証の環境では起きなかった。

ドメイン検証用の TXT レコードも Terraform で作れる。Security Agent に Target Domain を登録すると、検証用の TXT レコードの名前と値(トークン)が返る。コンソールで行う場合は、画面に表示された名前と値をコピーして DNS に登録する。この検証では、Route 53 のレコードの定義で、名前と値に Target Domain リソースが返す属性を参照させた。

resource "aws_route53_record" "domain_verification" {
  zone_id = local.dns.zone_id
  name    = awscc_securityagent_target_domain.app.verification_details.dns_txt.dns_record_name
  type    = awscc_securityagent_target_domain.app.verification_details.dns_txt.dns_record_type
  ttl     = 60
  records = [awscc_securityagent_target_domain.app.verification_details.dns_txt.token]
}

これにより、1 回の apply の中で Target Domain が作られ、返ってきた名前と値で TXT レコードが作られる。値を写す手作業も、.tf ファイルを書き換える作業もない。

最大タスク時間の設定

最大タスク時間は Terraform(awscc)のスキーマに存在せず、Terraform では設定できない(2026 年 10 月時点)。Web アプリか、CLI の update-pentest --max-task-hours で設定する。どちらでも 20 時間未満は受け付けない。「制限なし」のまま実行しないこと。

apply が 2 回に分かれる理由

ドメイン検証からペンテストの作成までは、次の順番で進む。

  1. Target Domain を作る。このとき、検証用の TXT レコードの名前とトークンが返る
  2. 返ってきた名前とトークンで、TXT レコードを DNS に置く
  3. ドメイン検証(Verify)を実行する。これは Security Agent に DNS の確認を指示する操作で、Security Agent が TXT レコードを引いて値が一致すれば即時に VERIFIED になる。コンソールでは Agent Space の Target Domains の一覧でドメインを選んで「Verify」を押す。CLI では aws securityagent verify-target-domain --target-domain-id td-<ID> を実行する。どちらも同じ API を呼ぶ。自動では行われず、どちらかの操作が要る
  4. Pentest を作る。ターゲットドメインが VERIFIED でないと作成は拒否される(実測では API が InvalidRequest で「The domain of the endpoint hasn’t been verified」を返した)

1・2・4 は Terraform で作れるが、3 は「作るもの」ではなく「実行する操作」なので、Terraform のリソースにはない。そのため、security-agent ディレクトリの apply は 3 を挟んで 2 回に分かれる。最初の apply で 1・2 に加えて IAM ロール、Application、Agent Space を作り(Application と Agent Space はターゲットドメインが PENDING のままでも作れた)、3 のあと 2 度目の apply で 4 の Pentest だけを作る。この切り替えのために、この検証の Terraform には変数 create_pentest(既定 false。true にすると Pentest を作る)を定義した。Security Agent 側の機能ではなく、この検証のコードの都合である。

なお公式ドキュメント(Enable an application domain for penetration testing)によれば、ドメインが同じアカウントの Route 53 に登録されている場合、コンソールの「One-click verification」で Security Agent が TXT レコードの作成と検証をまとめて行える。この検証ではコンソールを使わず Terraform と CLI で行ったので、この機能は試していない。

カスタムヘッダーの秘密値の受け渡し

「秘密値の扱い」で述べたとおり、WAF のヘッダー許可ルールと Pentest のカスタムヘッダーには同じ秘密値を設定する。この値は network-app ディレクトリで生成され、WAF のルールに入る。Pentest を作る security-agent ディレクトリは、network-app とは独立に apply するので、その値を network-app から受け取る仕組みが要る。

秘密値は network-app の output を terraform_remote_state で受け取る。その経路では sensitive 属性が外れるため、コード側で sensitive() を付けて plan に平文が出ないようにしている。

network-app を作り直すと、秘密値も新しく生成される。WAF と Pentest で値が食い違うので、続けて security-agent も apply して Pentest に新しい値を渡す必要がある。

Terraform の範囲外

コードセキュリティレビューのための GitHub 連携は、GitHub App のインストールでブラウザから github.com のページを操作する必要があるため、Terraform では行わない。GitHub 連携の登録(コンソールの「統合」の一覧に出るもの)と、それに接続したリポジトリは、Agent Space とは別のものとして保存される。コンソールでの操作が Agent Space 本体を書き換えると、Terraform が覚えている状態(state)と AWS 上の実体が食い違い、次の plan で差分が出て、apply でコンソールの変更が元に戻される恐れがある。実測では、コンソールでリポジトリを接続してコードレビューを有効にした後も、batch-get-agent-spaces が返す Agent Space の内容(名前、説明、登録したドメインとロール、更新時刻)は Terraform の state と一致したままで、接続したリポジトリは list-integrated-resources の側に現れた。コンソールでコードレビューを設定しても、Terraform で管理している Agent Space には影響しない。手順は GitHub 連携とコードレビューの手順(後日公開)に記載している。

自社で検証する際の構成の組み方

既存の Web サイトや API に当てることを考えると、導入の可否は次の 3 点で決まる。どれも技術より、組織として変更や利用が認められるかの問題である。

確認事項できない場合の代わり
WAF へのヘッダー許可ルールの追加WAF のない検証環境に当てる。または閉域方式(VPC の中にエージェントを置く)を検討する
DNS への TXT レコードの追加Web サーバーに確認用ファイルを置く(HTTP_ROUTE)。閉域方式なら VPC の中の名前解決で検証する
当てられる検証環境の有無本番には当てない前提なら、同じ構成の検証環境が要る。ない場合は使いどころがない

3 点目が要るのは、エージェントが実際に脆弱性を悪用して確かめるからである。AWS も、本番を反映した非本番環境でテストし、顧客データや本番にアクセスできる認証情報を置かないことをベストプラクティスとしている(Security best practices)。この検証でも、エージェントは実証のために利用者を 260 件以上登録し、コメントを投稿した。対象アプリのソースコードは通常はダウンロードできないが、ファイル取得の欠陥(パストラバーサル)を使って、公開する意図のないファイル(ソース、プロセスの環境変数、データベースのファイル)を取得した(経緯は検証結果を参照)。見つけた欠陥を次の調査の足がかりにするのは攻撃者と同じ振る舞いで、エージェントはそれを行う。テストに使う環境には、読まれて困るデータや認証情報を置かない。

エージェントが対象に及ぼす影響の範囲

公開方式のエージェントは AWS 側の環境で動き、利用者の側に作用する経路は 2 つである。サービスロールの権限で許したこと(評価ログの書き込みなど)と、対象アプリへの HTTP リクエストでできること(アプリが受け付けること。欠陥があれば、その欠陥でできることまで)で、それ以外の経路はない。

アプリに対する振る舞いについて、公式ドキュメント(Security Considerations)は次のように説明している。

  • 影響が最小の方法で探るよう指示されている。例として、SQL インジェクションを見つけたとき、テーブルを削除するのではなく SQL のバージョンを取り出す
  • 対象を落とさないためのガードレールがある。呼び出し回数の速度制御を含む。それでも、意図しない業務ロジックの相互作用はありうるため、本番前環境での実施を常に推奨する
  • SSRF(サーバーに別のサイトへアクセスさせる攻撃)のように、対象アプリに外部へリクエストを出させないと確認できない種類は、任意の宛先ではなく AWS が運用する collaborator エンドポイントに向けて行い、確認できた時点で悪用を止める

この検証で観測できた範囲(通信量、アプリへの書き込み、SSRF、AWS 側への作用)は検証結果の「エージェントの振る舞いで観測したこと」に記載している。

利用者の側でできる制御は 2 つある(Create a penetration test)。

  • スコープ外の指定: 破壊的な操作や管理機能のパスを「スコープ外」に指定すると、そのパスと配下のパスには一切アクセスしない。「アクセス可能」より優先される
  • 作ったデータのクリーンアップ: テスト中にエージェントは、対象アプリの中に利用者アカウントやフォームで送ったレコードなどを作る。「リソースを可能な限りクリーンアップ」(既定で有効)にすると、実行の最終段階でそれらの削除を試みる。手動停止した場合や、その段階の前に終わった場合は削除しない。削除は可能な限りであり、失敗しても実行は失敗にならないので、実行後にアプリを確認して残骸を消す必要がある。この検証では、対象アプリに利用者やコメントを削除する機能がなく、エージェントが登録した利用者は残った(EC2 の削除で消した)

もう 1 つ、費用の管理は利用者側の仕事である。終わる時刻は事前に分からず、最大タスク時間の最小値は 20 時間(定価 $1,000 相当)なので、それより低く抑えるには画面を見て手動で止めるしかない。CLI や API でも 20 時間未満の上限は付けられないことを確かめた。対象が広ければ、その分 Task-hour は増える。パス 8 つの対象アプリで完走までに消費した Task-hour は検証結果に記載している。

ソースや設計書を渡す選択肢

この検証では、ソースコードも設計書も API 仕様も渡さず、外から見える情報だけでエージェントに検査させた。Pentest の設定の「リソース」で、GitHub リポジトリ、ファイル、S3 の資料を参照資料として渡すこともできる。公式ドキュメントは、構成図、API 仕様(OpenAPI)、設定ファイル、GitHub リポジトリなどの補助資料は任意だが推奨であり、包括的な情報を与えると検査の網羅性が上がり、誤検知が減るとしている(Create a penetration test)。受け付ける資料として、ソースコード、脅威モデル、設計書、API 仕様が挙げられている(Security Considerations)。

この検証の結果(検証結果)では、エージェントはファイル取得の欠陥を使ってソースを自力で取得していた。ソースを渡しておけば、欠陥の有無にかかわらず同じ情報を最初から使える。ただし、渡すリポジトリに答えや秘密情報を含めないことは、GitHub 連携とコードレビューの手順(後日公開)の手順 1 と同じ注意が要る。

Pentest の単位

公開 API のパスが大量にあっても、パスごとに Pentest を作る必要はない。Pentest に登録するのはターゲット URL(ドメインや基点となる URL)で、配下のエンドポイントはエージェントが Static analysis フェーズで自分で列挙する。検証済みドメインのサブドメインは追加の検証なしに対象にでき、1 つの Pentest に複数のターゲット URL を登録できる。

単位目安
Agent Space対象アプリケーション(システム)ごと。許可するドメイン・VPC・ロールの境界になる
Pentestアプリケーションの環境や条件ごと。例: ステージングの未認証テスト、ステージングの一般ユーザー権限でのテスト
RunPentest を実行するたびに 1 つ。リリースごと、月次などの周期で同じ Pentest を再実行する

ただし、対象が広いほど Task-hour(料金)は増える。公式ドキュメントは費用を抑える手段として、ドメイン全体ではなく特定のパスをターゲットにすること、検査するリスク種別を絞ること、破壊的な操作のパスを Out of scope に指定することを挙げている。API の仕様書(OpenAPI)を参照資料として渡すと、列挙の精度が上がり、無駄な探索が減る。

本記事の構成を実際に作って実行する手順は、ペンテストの手順に記載している。

参考リンク

Amazonアソシエイトリンク