Amazon Web Services ブログ

AWS Continuum で実現するデプロイごとのペネトレーションテスト

本ブログは 2026 年 10 月 14 日に公開された AWS Blog “Penetration testing every deployment with AWS Continuum” を翻訳したものです。

静的解析 (SAST)、ソフトウェア構成分析 (SCA)、動的スキャナー (DAST) など、CI/CD パイプラインに組み込めるセキュリティツールの多くは、パターンやシグネチャに基づいて潜在的な問題にフラグを立てます。フラグが立てられたパターンは調査すべき箇所を示しますが、実際に悪用可能かどうかを確認するのはペネトレーションテストの役割です。従来、この確認は、デプロイのたびではなく、スケジュールに沿って定期的に実施される、パイプラインとは別枠の診断によって行われてきました。そのため、診断と診断の間に行われたデプロイは、確認が済まないまま本番環境に到達していました。

AWS Continuum for penetration testing (旧称 AWS Security Agent) は、AI エージェントを使用して、実行中のアプリケーションに存在する悪用可能な脆弱性を検出し、確認するサービスです。この記事では、このサービスをパイプラインのデプロイ後ゲートとして組み込む方法を説明します。このゲートは、直前にデプロイした変更に自動的にスコープを限定したペネトレーションテストを実行し、検出結果の重要度が指定したしきい値以上の場合は、本番環境への昇格をブロックできます。各実行では、アプリケーション全体ではなく、前回ゲートを通過して以降の変更のみをテストするため、デプロイのたびに実行できるほど高速かつ経済的です。この記事では、主要な 4 つの CI/CD プラットフォーム (GitHub Actions、GitLab CI/CD、Bitbucket Pipelines、Azure DevOps Pipelines) に適用できるこのパターンの概要を説明したうえで、GitHub Actions を使用した具体的な例を紹介します。

この記事を読み終えるころには、変更をステージング環境にデプロイし、デプロイした変更に対してスコープを限定したペネトレーションテストを実行して、ゲートを通過した場合にのみ変更を本番環境に昇格させるパイプラインが完成します。

次の図は、エンドツーエンドのフローを示しています。CI/CD パイプラインがステージング環境にデプロイすると、ペネトレーションテストゲートのステップが OpenID Connect (OIDC) を使用して AWS アカウントに対して認証し、Continuum for penetration testing を呼び出します。Continuum for penetration testing は、ステージング環境に対してスコープを限定したテストを実行します。ゲートは結果を待ってパイプラインに報告し、結果に応じて本番環境への昇格をブロックします。テストはすべて AWS アカウント内のサーバー側で実行されるため、CI/CD ランナー上でペネトレーションテストのロジックが実行されることはありません。

Figure 1: Pipeline gate architecture: Your CI/CD pipeline deploys to staging, the pentest gate authenticates via OIDC and calls Continuum for penetration testing, which returns a scope decision that gates promotion to production.

図 1: パイプラインゲートのアーキテクチャ: CI/CD パイプラインがステージング環境にデプロイし、ペネトレーションテストゲートが OIDC で認証して Continuum for penetration testing を呼び出します。Continuum for penetration testing は、本番環境への昇格可否を左右するスコープ判定を返します。

変更にスコープを限定することが重要な理由

ゲートをデプロイのたびに実行できるのは、各テストのスコープを直前にデプロイした変更に限定しているためです。Continuum for penetration testing はコミット範囲を決定し (head はゲートの対象となるコミット、base はゲートが前回通過を認めたコミットです)、変更された部分のみをテストします。アプリケーション全体ではなく変更をテストすることで、デプロイごとのペネトレーションテストを、四半期ごとの定期作業ではなくパイプラインに組み込めるほど短時間かつ低コストで実行できます。

各実行では、3 つのスコープ判定のいずれかが返されます。具体的には、in scope (スコープ内: 変更によってアタックサーフェスが変化しており、検出結果がしきい値に基づいて評価されます)、scoped out (テスト不要: 変更はスコープ内ですが、セキュリティに関係する変更がないため、パイプラインは通過します)、scope conflict (スコープの競合: 変更が設定済みのスコープ外にあるためテストされておらず、これをブロック対象にするかどうかを選択できます) です。Continuum for penetration testing コンソールでは、各実行の [Scoping] タブに判定結果とコミット範囲が記録されます。そのため、ゲートがブロックした場合は、その理由を正確に確認できます。コミット範囲の計算方法と検証方法の詳しい仕組みについては、「How the pipeline integration scopes the test」を参照してください。

ゲートは、パターンマッチではなく、Continuum for penetration testing が悪用可能であると検証した検出結果に基づいてブロックします。そのため、パイプラインが赤 (失敗) になった場合の意味は明確です。つまり、変更に再現可能な脆弱性が存在し、昇格前に修正が必要だということです。

ゲートのセットアップ

ゲートのセットアップは 3 つのステップで完了します。ペネトレーションテストの設定、パイプラインが引き受け可能な AWS Identity and Access Management (IAM) ロールの作成、パイプラインへのゲートステップの追加です。

開始する前に、IAM OIDC ID プロバイダーと IAM ロールの両方を作成するアクセス許可を持つ AWS アカウントと、ペネトレーションテストから到達可能なステージング環境を用意してください。

ステップ 1: ペネトレーションテストを設定する

「Before you start」の説明に従って、1 回限りのセットアップを完了します。具体的には、ターゲットドメインを検証し、Agent Space でペネトレーションテストを作成し、CI/CD モードを有効にして、リポジトリを接続します。Agent Space ID (as-…) とペネトレーションテスト ID (pt-…) は、ステップ 3 でゲートステップに渡すため、控えておいてください。パイプラインがテストするのは、このペネトレーションテストで設定された対象です。そのため、ゲートがカバーする範囲は、このペネトレーションテストのターゲットとスコープによって決まります。

ステップ 2: IAM ロールを作成する

認証には、保存したキーではなく OIDC フェデレーションを使用します。CI/CD プロバイダーがパイプラインを識別する有効期間の短いトークンを発行し、事前に作成した IAM ロールをパイプラインが引き受ける際に、AWS Security Token Service (AWS STS) がそのトークンを一時的な認証情報と交換します。長期間有効な AWS アクセスキーを CI/CD 変数として保存する必要がないため、パイプラインから漏えいすることが最も多い認証情報をなくすことができます。

ゲートのセキュリティは、IAM ロールの 2 つのポリシーに依存します。信頼ポリシー (どのパイプラインがロールを引き受けられるか) とアクセス許可ポリシー (ロールを引き受けた後にパイプラインが実行できる操作) です。正確なポリシー JSON、プロバイダー固有の OIDC URL と audience の値、各 CI/CD プラットフォームの sub クレームの形式については、「Scope the trust policy to your pipeline」と「Grant least-privilege permissions」のリファレンスに従ってください。

これらのポリシーのスコープを決める際の判断ポイントを次に示します。

最初に判断するのは、信頼ポリシーのスコープをどこまで厳密にするかです。このパイプラインでデプロイするブランチが 1 つだけであれば、sub クレームをそのブランチ (例えば、main) に固定するのが適切なデフォルトです。複数のブランチや環境で正当にゲートを実行する場合は、プロバイダーのクレームが対応していれば、ブランチではなく環境単位でスコープを限定してください。なお、すべてのプロバイダーがクレームにブランチを含めるわけではない点に注意してください。Bitbucket では、リポジトリ (およびオプションでデプロイ環境) が最も厳密なスコープとなります。まずは実際のデプロイ状況が許す限り狭い範囲から始め、実際のブランチや環境で必要になった場合にのみ範囲を広げてください。また、2026 年 7 月 15 日以降に作成または名前変更された GitHub リポジトリは、数値 ID を含むイミュータブルな sub クレームを発行します。ポリシーを作成する前に、パイプラインが実際に発行する sub を確認してください。

ステップ 3: パイプラインにゲートステップを追加する

ステップの考え方はどのプロバイダーでも同じで、異なるのは構文だけです。アップストリームの変更によってパイプラインで実行される内容が変わらないよう、インテグレーションはイミュータブルなバージョン (リリースタグ、コミット SHA、または固定したコンポーネントバージョン) に固定してください。設定の完全なリファレンスとデフォルト値の一覧については、「Defaults the integration ships with」を参照してください。

ゲートを本格的に運用する前に、ドライランモードで一度実行してください。ドライランでは、スコープを限定したペネトレーションテストのジョブを開始することなく、認証が成功すること、リージョンの指定が正しいこと、および対象のリポジトリを正しく特定できることを確認します。検証に失敗した場合は、エラー処理の設定にかかわらずステップが失敗するため、設定ミスにすぐ気付くことができます。

具体的な例: GitHub Actions

次のワークフローは、ゲートを機能させるための 3 つのジョブ構成を示しています。deploy はステージング環境へのデプロイ、security-pentest はゲートの実行、deploy-prod は本番環境への昇格を担当します。deploy-prod は needs: [security-pentest] を宣言しているため、ゲートが失敗すると GitHub は本番環境へのデプロイをスキップします。

注: チュートリアルでは、ゲートがブロックする実際の検出結果を得るために、意図的に SQL インジェクションの脆弱性を含めたアプリケーションを使用しています。このアプリケーションは、隔離された非本番環境にのみデプロイし、作業が終わったら削除してください。

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: staging
    steps:
      - uses: actions/checkout@v4
        with:
          persist-credentials: false
      - run: ./deploy.sh staging

  security-pentest:
    needs: [deploy]
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: write
      actions: read
      deployments: read
    steps:
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::111122223333:role/my-pentest-gate-role
          aws-region: us-west-2
          role-duration-seconds: 7200
      - uses: aws-actions/aws-continuum-run-pentest@v1
        with:
          agent-space-id: as-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
          pentest-id: pt-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
          severity-threshold: HIGH
          on-scope-conflict: block
          timeout-minutes: "45"

  deploy-prod:
    needs: [security-pentest]
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4
        with:
          persist-credentials: false
      - run: ./deploy.sh production

アカウント ID、ロール名、リージョン、as-/pt- 識別子は、実際の値に置き換えてください。

この例では、2 つの点に注意してください。まず、contents: write アクセス許可によって、ゲートはリポジトリ内に ref として保持しているベースラインマーカーを進めることができます。書き込みアクセス権がないと、毎回の実行で意図よりも広い範囲がテストされてしまいます。次に、認証情報ステップの role-duration-seconds: 7200 は、ペネトレーションテストの所要時間より長くする必要があります。ロールで許可されている上限を超える期間をリクエストすると、AWS STS は期間を短縮するのではなくリクエストを拒否します。そのため、ロールの最大セッション期間は少なくとも (タイムアウト + 10) x 60 秒に設定してください。途中で認証情報の有効期限が切れると、本来は正常な実行が、あたかもサービス側に問題があるかのように失敗します。さらに、クリーンアップ処理も、取り残されて実行を続けているジョブを停止できなくなります。

4 つのプロバイダーすべてについて、詳細な手順を解説したチュートリアルへのリンクをこの記事の最後に掲載しています。

ゲートのロールアウト: レポート専用モードからブロック運用へ

ロールアウトの前に、最も重要なポイントを押さえておきましょう。

ステップが失敗するかどうかは 3 つの独立した設定によって決まり、それぞれ異なる結果を対象としています。severity-threshold (検出結果の扱い)、on-scope-conflict (変更がテストされなかった場合の扱い)、fail-on-infrastructure-error (ジョブを実行できなかった場合の扱い) です。これらは互いに独立しているため、1 つを緩和しても他の設定には影響しません。

多くのチームは、まずレポート専用モードでゲートを導入します。しきい値を NONE に設定すると、テストは実行されて検出結果も報告されますが、パイプラインはブロックされません。1~2 回のリリースで検出内容を確認してから、しきい値を HIGH に変更して、判定を厳しくします。この段階的なロールアウトは妥当な方法ですが、重要な注意点が 1 つあります。

ゲートを通過した実行は、レポート専用モードでの実行も含めて、ベースラインを進めます。その実行の範囲に含まれるコミットは通過済みとして記録されるため、後でしきい値を厳しくしても再テストされません。例えば、1 か月間レポート専用モードで運用した後に HIGH に切り替えた場合、ゲートが評価するのは最後のレポート専用モードでの実行以降のコミットのみです。既にレポート済みの内容まで遡って再チェックすることはありません。

これは設計上の動作です。設定を緩和するということは、その設定で許容される結果を受け入れることを意味します。ただし、以前の実行で通過済みとなった範囲を、より厳しい設定で再評価したい場合は、head と base のコミット SHA をオーバーライドしてください。具体的には、base-commit-sha と head-commit-sha を設定します。マーカーの保存場所はプロバイダーによって異なります。プロバイダーごとの対応表については、「What advances the baseline, and why that matters」を参照してください。

また、注意すべきデフォルト値も 1 つあります。fail-on-infrastructure-error のデフォルト値は false (フェイルオープン) です。昇格にセキュリティ承認が必要なパイプラインでは、最初から true に設定してください。

GitLab: コミットした設定はデフォルト値にすぎず、保証ではない

GitLab CI/CD では、パイプライン変数が、ジョブ自身の variables: ブロックで設定された値よりも優先されます。つまり、パイプラインを実行できるユーザーであれば誰でも、しきい値を下げる、ドライランを有効にする、エラー処理を緩和するといった変数を指定できてしまいます。その場合、ゲートは .gitlab-ci.yml の記述ではなく、変数の指定どおりに動作します。リリースを止める役割をゲートに任せる前に、プロジェクトの CI/CD 設定で、変数をオーバーライドできるユーザーを制限してください。

ランナー上の認証情報の保護

ステップの実行中は、一時的な認証情報と OIDC トークンを機密情報として扱う必要があります。次の 3 つの対策が重要です。

  • デバッグと詳細トレースを無効にしておく: 認証情報を扱うステップが対象です。シェルのトレースモード (例えば、GitLab CI の CI_DEBUG_TRACE) は、コマンド引数を平文でジョブログに出力します。
  • ソース管理の認証情報をワークスペースに残さない: チェックアウトステップで persist-credentials: false を設定し、後続のステップから読み取れるランナーの .git 設定にトークンが書き込まれないようにします。
  • 信頼できないステップにトークンを公開しない: OIDC トークンのアクセス許可は必要なジョブにのみ付与し、トークンを読み取れるジョブでは信頼できないコード (フォークのビルドなど) を実行しないようにします。

本番環境とマルチアカウント構成への適用

この例では、わかりやすくするために 1 つのアカウントを使用しています。実際には、ステージング環境と本番環境を別々の AWS アカウントに分けることも多いでしょう。その場合も、このパターンは次のように無理なく拡張できます。

  • ペネトレーションテストゲート用の IAM ロールは、ペネトレーションテストと Agent Space が存在するアカウント (セキュリティアカウント) に作成します。パイプラインは、どのワークロードアカウントにデプロイする場合でも OIDC を介してこのロールを引き受けます。そのため、各アカウントにロールのコピーや長期間有効なキーを置く必要がなく、適切にスコープを設定した 1 つのゲートロールを維持するだけで済みます。
  • 昇格の経路にある本番前の各環境に対してゲートを実行し、その結果に基づいて次のステージへの昇格を制御します。ベースラインはゲートを通過した場合にのみ進むため、デプロイをまたいでもカバレッジが途切れることはありません。
  • ゲートが実際のリリースを止める役割を担うようになったら、fail-on-infrastructure-error を true に設定し、パイプライン変数をオーバーライドできるユーザーを制限します。
  • アップストリームの変更が実行前にレビューされるよう、インテグレーションのバージョンを固定します。

AWS Security Agent に関するより幅広いガイダンス (非本番環境に対するテスト、AI が生成した検出結果の検証、IAM など) については、「Security best practices for AWS Continuum」を参照してください。

クリーンアップ

ゲートを試すためにいずれかのプロバイダー向けチュートリアルを実施した場合は、そのチュートリアルの「Clean up」セクションの手順に従って、作成したリソースをすべて削除してください。対象は、サンプルアプリケーションのステージング環境、IAM ロールと OIDC ID プロバイダー、そして (ウォークスルー専用に作成した場合は) Continuum for penetration testing コンソールのペネトレーションテスト (および専用の Agent Space) です。これらを削除することで、継続的な料金の発生を防ぎ、意図的に脆弱性を含めたサンプルアプリケーションをオフラインにできます。

まとめ

ペネトレーションテストのスコープをデプロイした変更に限定することで、四半期ごとの取り組みではなく、デプロイごとのゲートとしてペネトレーションテストを実用的に運用できるようになります。押さえておくべき重要な判断は次のとおりです。

  1. 厳格さ: レポート専用モード (NONE) から始めます。ただし、ベースラインが進む点に注意し、しきい値を厳しくする際にはマーカーを削除してください。
  2. 失敗時の動作: セキュリティ承認が必要なものについては、fail-on-infrastructure-error を true に設定します。
  3. 信頼のスコープ: sub クレームは、実際のデプロイ状況が許す限り狭く固定します。
  4. ロールの配置場所: セキュリティアカウントに 1 つのロールを配置し、各ワークロードのパイプラインから OIDC を介して引き受けます。

お使いのプラットフォームでゲートをエンドツーエンドで構築するには、プロバイダーに対応するチュートリアルを参照してください。

概念の説明と完全なリファレンスについては、「Run penetration tests from your CI/CD pipeline」を参照してください。


Jigar Bhatt

Jigar Bhatt

Jigar は AWS Continuum for Penetration Testing のシニアソフトウェアエンジニアです。AI を活用したペネトレーションテスト機能を開発し、お客様がアプリケーション内の悪用可能な脆弱性を検出して修正できるよう支援しています。AI セキュリティの分野に携わる前は、AWS CodeBuild で数年間にわたり開発者ツールの構築に従事していました。インドの BITS Pilani でソフトウェアシステムの工学修士号を取得しています。

Benjamin-Li

Benjamin Li

Benjamin は AWS Continuum for Penetration Testing チームのソフトウェアエンジニアで、AI を活用したペネトレーションテスト機能を開発しています。UW で数学の学士号を取得しました。

本ブログは Security Solutions Architect の 中島 章博 が翻訳しました。