はじめに
AWS Continuumのペネトレーションテストは、インターネットからアクセスできるエンドポイントに対しても、VPC内でプライベートに動いているアプリに対しても実行できる。
AWS上で動いている業務アプリは社内ネットワークの外からアクセスできない(インターネットからアクセスできない)ことも多いので、Internal ALBの後ろにあるアプリに対してテストを実行する方法をリサーチ。
テストは、自分のAWSアカウント内に立てた検証環境に対してだけ実施している。ペネトレーションテストは、自分が管理しているか、許可を得たシステムに対してだけ実施すること。
この記事で分かること
- 閉域のアプリにContinuumのペンテストを当てるまでの設定の流れ(エージェントスペースコンソール側とウェブアプリ側の設定)
- ターゲットドメインを「プライベート VPC」で検証して、VPCを設定するところ
- 内部ALBをHTTPSで使うための、ドメインと証明書の準備
- 設定でつまずいたところと、CloudTrailで原因を探す方法
構成
※ドメイン名はダミー
| 要素 | 内容 |
|---|---|
| アプリ | OWASP Juice Shop(ECS Express Modeで立てたECSサービス) |
| 入口 | 内部ALB(HTTPS 443) |
| ドメイン |
internal.example.com(Route 53で、内部ALBに向けたレコード) |
| 証明書 | ACMの公開証明書(internal.example.com) |
| Continuum側 | ターゲットドメインを「プライベート VPC」で検証 + VPCの設定 |
| サブネット | エージェント用にプライベートサブネット2つ(2AZ) |
内部ALBとECSサービスのつなぎ方は、この記事では扱わない。ECS Express Modeのサービスは、ロードバランサーの設定をあとから変えられないので、同じ構成を作るときは注意。
手順
1. 内部ALBに、テスト用のドメインと証明書を用意する
内部ALBのDNS名(internal-xxxx.ap-northeast-1.elb.amazonaws.com)はAWSのドメインなので、ACMで証明書を発行できない。
HTTPSで使うなら、自分のドメインのサブドメインを用意する。
- ACMで
internal.example.comの公開証明書をリクエストする(DNS検証) - 内部ALBの443番のリスナーに、その証明書を追加する
- もともと付いてる証明書はそのままでいい。ALBは接続時のホスト名(SNI)を見て、合う証明書を選んでくれる
- Route 53で、
internal.example.comを内部ALBに向けるレコードを作る
VPCの中から internal.example.com で内部ALBに届くことを確認しておく。
2. ターゲットドメインを「プライベート VPC」で検証して、VPCを設定する(エージェントスペースコンソール側)
この設定をしないと、ウェブアプリ側でテストの詳細を設定するときにVPCが選べない。画面には「管理者に連絡して、エージェントスペースコンソールで VPC をセットアップします」と出る。
エージェントスペースコンソール側のペネトレーションテストの設定で、internal.example.com を登録し、検証方法を 「プライベート VPC」 にする。
設定の時点では 到達不能 と出るけど、VPCの中からしか届かない対象なので、ここでは問題ない。
検証はペンテストを実行するたびにやり直されるので、そのまま進む。
画面の「ステータスの理由」にも「Target domain is usable for private VPC pentests.」と出ている。
3. テストの詳細設定でVPCを設定する(ウェブアプリ側)
ペネトレーションテストの詳細設定の「VPC リソース」で、VPC、サブネット、セキュリティグループを選ぶ。
エージェントスペースコンソール側で設定したVPCが選択できるようになっている。
サブネットとセキュリティグループも設定する。
4. 認証情報を設定する(ウェブアプリ側)
ログインしないと見えない画面やAPIもテストしたいなら、認証情報を渡す。渡さない場合、テストできるのはログインなしで見えるページとAPIだけになる。
テスト用のアカウントを用意する
アプリにテスト専用のアカウントを作っておく。ドキュメントでも、個人のアカウントや管理者アカウントじゃなくて、テスト用のアカウントを使うよう書かれている。
今回はJuice Shopに一般ユーザー(Customer)のアカウントを2つ作った。
(あるユーザーが別のユーザーのデータを触れてしまわないかもテストできるかなと思ったので)
内部ALBにはVPCの中からしか届かないので、登録はCloudShellのVPC環境から行った。
Juice Shopの秘密の質問の一覧を確認する
登録には秘密の質問のIDが必要なので、先に秘密の質問一覧を取得。
curl -s https://internal.example.com/api/SecurityQuestions/ | python3 -c "import sys,json; [print(q['id'], q['question']) for q in json.load(sys.stdin)['data']]"
アカウントを登録する
メールアドレス、パスワード、秘密の質問のID、答えを置き換えて実行。
curl -s -X POST https://internal.example.com/api/Users/ -H 'Content-Type: application/json' -d '{"email":"<メールアドレス>","password":"<パスワード>","passwordRepeat":"<パスワード>","securityQuestion":{"id":<質問のID>},"securityAnswer":"<答え>"}'
ログインできるか確認する
curl -s -X POST https://internal.example.com/rest/user/login -H 'Content-Type: application/json' -d '{"email":"<メールアドレス>","password":"<パスワード>"}' | python3 -c "import sys,json; print('OK' if 'authentication' in json.load(sys.stdin) else 'NG')"
OK が出れば、Continuumに渡す認証情報として使える。
認証情報を登録する
ペネトレーションテストの詳細設定の「認証情報」で登録する。渡し方は大きく2つ。
| 渡し方 | 向いている場面 |
|---|---|
| ユーザー名とパスワードを直接入力する | 開発環境やテスト環境 |
| Secrets Managerのシークレット、または認証情報を返すLambda関数のARNを入力する | 本番環境や、扱いに気を使う認証情報 |
今回は直接入力したが、一覧には「Secrets Manager のシークレット」と表示されていたので、裏でSecrets Managerのシークレットとして保存されてるっぽい。

ログインの手順を指示する
認証情報をどう使ってログインするか(ログイン画面のURL、入力する項目など)は、エージェントスペースのログインプロンプトに書いておく。
今回はこう書いた。パスワード変えたりアカウント消さないでね、っていうのも一応書いておいた。
Log in at https://internal.example.com/#/login using the "Email" and "Password" fields, then click "Log in".
Two customer test accounts are provided. Do not change their passwords or delete them.
サインインできるかを確かめる
登録した認証情報は、一覧の「テスト」で、実際にサインインできるかを先に確かめられる。

画面の説明だと、たいていのサインインのテストは2〜3分で終わって、サインインできない場合は15分で打ち切られる。今回は体感3分かからないくらいですぐに確認終わった。
複数の認証情報を登録する場合、同様の手順を実施。
Juice Shopを初期化すると、登録したアカウントも消える。
アプリの初期化を挟んで複数回テストをするときは、テストの前にサインインできるかを確かめておくと安心。
5. テストを実行する
設定が完了したら「実行を開始する」をクリックしてテストを実行する。
実行中の画面。プリフライトのEndpoint accessibility validationも問題なく完了している。

プリフライトのあとは、ラウンドごとに検出、計画、検証が進んでいく。

ハマったところ
AWS Continuumに限らないものもあるけど。
「統合」のプライベート接続は要らなかった
エージェントスペースの「統合」→「プライベート接続」にも、VPC Latticeを使ってエージェントの通信をVPCの中のエンドポイントまで届ける機能がある。
名前からして閉域のテストにはこれが要ると思って最初に作ったけど、消してもテストは実行できた。
ドキュメントを見ると、プライベート接続は、社内に置いたGitHub Enterprise ServerやGitLab Self-Managedなどのソースコード管理のツールにContinuumをつなぐためのものだった(Connect a private connection)。ペンテストの対象に届くようにするのは、「プライベート VPC」の検証とVPCの設定のほう。
プライベート接続のホストのアドレスには、ホスト名だけを入れる
要らなかったプライベート接続だけど、作るときにもハマった。プライベート接続の設定時、サービスターゲットの詳細としてホストアドレスを入力する必要がある。
ここに https://internal.example.com と入れると、作成に失敗する(正しくは internal.example.com)。
画面には「作成に失敗しました」としか出なかったので、CLIでCloudTrailを探した。
aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=CreatePrivateConnection --max-results 5 --query 'Events[].CloudTrailEvent' --output json | python3 -c "import sys,json; [print(json.dumps({k:e.get(k) for k in ('eventTime','errorCode','requestParameters','responseElements')}, ensure_ascii=False, indent=2)) for e in map(json.loads, json.load(sys.stdin))]"
CloudTrailのイベントには、APIに送った内容(requestParameters)と、APIが返した内容(responseElements)が残るので、responseElements を見るとエラーの詳細が入っていた。
Value 'https://...' at 'mode.serviceManaged.hostAddress' failed to satisfy constraint: Member must satisfy regular expression pattern: [a-zA-Z0-9.:\-]+
サブドメイン用のホストゾーンは、親のゾーンから委任しないと引けない
internal.example.com 用に、別のホストゾーンを作ってレコードを入れたけど名前解決できなかった(NXDOMAIN)。
Route 53でサブドメイン用にホストゾーンを分けるときは、親の example.com のゾーンにNSレコードを作って委任する必要がある(Routing traffic for subdomains)。
aws route53 list-hosted-zones-by-name --dns-name example.com --query 'HostedZones[].{id:Id,name:Name,private:Config.PrivateZone}' --output table
上のコマンドで、同じドメインにゾーンが複数ないかを確認できる。
レコードが1つ2つなら、親のゾーンにまとめるのが一番シンプル。ACMのDNS検証用のCNAMEも同じ理由で通らなくなるので注意。
ウェブアプリ側でVPCが選べない
ウェブアプリの「VPC リソース」を開いても、VPCの選択肢が空だった。
原因は、エージェントスペースコンソール側で、ターゲットドメインを「プライベート VPC」で検証して、VPCを設定していなかったこと。最初は検証方法を「DNS テキストレコード」にしていた。
VPCの設定は、「プライベート VPC」で検証したドメインを、VPCの中からテストするためのもの。
エージェントスペースコンソール側で検証方法を「プライベート VPC」に変えて、VPCを設定したら、ウェブアプリ側でもVPCが選べるようになった。
まとめ
- VPCの中のアプリは、「プライベート VPC」検証 + VPCの設定で、Continuumからテストを当てられる。今回の構成なら、プライベート接続は要らなかった
- 内部ALBをHTTPSで使うなら、自分のドメインのサブドメインと証明書を用意して、Route 53で内部ALBに向けておく
- 設定で失敗して画面に理由が出ないときは、CloudTrailの
requestParametersとresponseElementsを見ると原因が分かることが多い
参考
- Enable penetration test(AWS Security Agent ユーザーガイド)
- Provide authentication credentials for penetration testing(AWS Security Agent ユーザーガイド)
- Connect a private connection(AWS Security Agent ユーザーガイド)
- Resources created by Amazon ECS Express Mode services
- Routing traffic for subdomains(Amazon Route 53 開発者ガイド)
- Penetration testing a private application with AWS Security Agent(AWS Builder Center)





