Fortune gate
← ブログ一覧

セキュリティ

クラウドで脆弱性診断を自動化!OWASP ZAP × Azure Runbook × ACI 実践ガイド

セキュリティ# 脆弱性診断# Azure
インフラチーム
Fortune gate株式会社
2026年5月19日

1. はじめに

Webアプリケーションへの攻撃は高度化しており、継続的な診断と改善が必須となっています。OWASP ZAPはオープンソースの脆弱性診断ツールで、GUI操作だけでなくクラウド環境への統合も可能です。

本記事では「Azure Automation Runbook × Azure Container Instance × Azure Storage」を組み合わせて、"OWASP ZAPによる脆弱性診断を完全クラウド上で自動実行する構成"を紹介します。GUI操作を排除し、運用負荷を軽減しながら継続的なセキュリティチェックを実現できます。

診断頻度(月1回や四半期ごと)はシステムの重要度に応じて柔軟に設定可能であり、本記事はセキュリティ診断の自動化に関心のある方、またはAzureのRunbookやACIの実践経験を積みたい方に適しています。

2. 脆弱性診断の自動実行のメリット

手動診断では毎回同じ作業の繰り返しが必要であり、実施漏れやタイミングのズレが発生するリスクがあります。自動化により以下が実現します:

  • 定期実行の保証:スケジュールに従って診断が実行されるため、忘れたり後回しにされることがありません
  • 最新エンジン使用:OWASP ZAPの公式Dockerイメージを毎回取得し、バージョンアップの見落としを防止
  • 運用負荷軽減:GUI操作や手動レポート取得が不要となり、結果の確認と対応に集中可能
  • セキュリティ意識の定着:定期診断が習慣化することで、組織のセキュリティ意識が向上

3. 全体構成

システムはAzure Automation Runbookをトリガーとして、ACI上でOWASP ZAPを起動し、診断結果をAzure Blob Storageに保存します。

主要コンポーネント:

サービス名

概要

Azure Automation Runbook

処理制御(SASトークン生成、ACI起動)

Azure Container Instance(ACI)

OWASP ZAPのコンテナ実行環境

Azure Blob Storage

診断結果の保存先

Docker Hub

ZAP公式イメージ(zaproxy/zap-weekly:latest)

4. ハンズオン手順

ステップ1: ストレージアカウントの準備

セキュリティを考慮した設定を実施します:

  • ストレージアカウントをプライベート設定
  • 外部からのアクセスはSASトークン経由のみ許可
  • Blobコンテナを非公開(Private)設定

SAS(Shared Access Signature)トークンとは:

有効期限を設定可能(例:生成から1時間)で、操作の制限(読み取り・書き込み・削除など)を細かく制御できます。URLに埋め込んで使用するため、HTTPリクエスト時に簡単に利用できます。

Azure Portalから[ストレージアカウント]を作成し、以下の設定を確認します:

  • [個々のコンテナーでの匿名アクセスの有効化を許可する]のチェックを外す
  • ネットワークはデフォルト設定でOK
  • コンテナを作成し、匿名アクセスレベルが「プライベート」であることを確認

ステップ2: Azure Automationアカウントの作成

Azure Automationはクラウド上で定期的なタスクや運用処理を自動化するサービスです。その中核がRunbookで、PowerShellやPythonのスクリプトを記述・実行する単位です。

作成手順:

  1. Azure Portalから[Automation アカウント]を作成
  2. [マネージドID]-[システム割り当て]にチェックを入れる
  3. ネットワーク接続は[パブリックアクセス]を選択

ステップ2.1 マネージドIDの設定

AzureマネージドIDはAzureリソースが安全に他のAzureサービスにアクセスするための仕組みです。以下の処理が可能になります:

  • RunbookがACIを作成/起動する
  • RunbookがBlobストレージにアクセスするためのSASトークンを生成する

ロール付与手順:

  1. Automationアカウントの[アカウント設定] → [ID] → [Azure ロールの割り当て]へ移動
  2. [ロールの割り当ての追加]をクリック
  3. スコープを「リソースグループ」に設定
  4. 1つ目のロール:Azure Container Instances Contributor Role(ACIリソースへの共同作成者権限)
  5. 2つ目のロール:ストレージ BLOB データ共同作成者(Blobストレージへのアクセス権限)

ステップ2.2 Automation Runbookの作成

Automationアカウントの左メニューから[プロセスオートメーション] → [Runbook] → [Runbookの作成]を選択し、以下を設定:

  • 名前:任意
  • Runbookの種類:PowerShell
  • ランタイムバージョン:7.2

作成完了後、編集画面でスクリプトを記述できます。

ステップ3: 脆弱性診断自動実行スクリプトの作成

Runbookで実行される処理の流れ:

  1. OWASP ZAPのDockerイメージを使ってACIを作成
  2. 指定した診断対象に脆弱性スキャンを実行
  3. スキャン結果(HTML形式)をBlob Storageにアップロード

サンプルコード全体:

# 認証(マネージド ID)
Connect-AzAccount -Identity

$context = Get-AzContext

# ストレージ情報
$resourceGroup = "<RESOURCE_GROUP_NAME>"
$storageAccountName = "<STORAGE_ACCOUNT_NAME>"
$containerName = "<BLOB_CONTAINER_NAME>"
$location = "<LOCATION>"
$targetUrl = "<TARGET_URL>"
$containerGroupName = "<CONTAINER_GROUP_NAME>"
$imageName = "zaproxy/zap-weekly:latest"

Write-Output "START!!!"

# ストレージアカウントのコンテキスト取得
$context = New-AzStorageContext -StorageAccountName $storageAccountName -UseConnectedAccount
Write-Output "コンテキスト取得完了"

# Blobに格納するファイル名の生成
$timestamp = Get-Date -Format "yyyy-MM-dd-HHmm"
$blobName = "report-$timestamp.html"

# SAS トークン生成
$expiryTime = (Get-Date).AddHours(1)
$sasToken = New-AzStorageBlobSASToken `
  -Container $containerName `
  -Blob $blobName `
  -Context $context `
  -Permission rwd `
  -ExpiryTime $expiryTime `
  -FullUri

# 環境変数定義
$envVars = @(
    New-AzContainerInstanceEnvironmentVariableObject -Name "TARGET_URL" -Value "$targetUrl"
    New-AzContainerInstanceEnvironmentVariableObject -Name "SAS_URL" -Value "$sasToken"
)

# コンテナポート定義
$containerPort = New-AzContainerInstancePortObject -Port 8080 -Protocol TCP

# コンテナ定義
$container = New-AzContainerInstanceObject `
  -Name "zap" `
  -Image $imageName `
  -Command @(
    "sh", "-c",
    'echo "Running ZAP..." && zap.sh -cmd -quickurl "$TARGET_URL" -quickout /home/zap/report.html && echo "ZAP scan completed" && ls -l /home/zap/report.html && curl -v -X PUT -T /home/zap/report.html "$SAS_URL"'
  ) `
  -EnvironmentVariable $envVars `
  -RequestCpu 1 `
  -RequestMemoryInGb 2 `
  -Port @($containerPort)

# コンテナグループポート定義
$groupPort = New-AzContainerGroupPortObject -Port 8080 -Protocol TCP

# コンテナグループ作成
New-AzContainerGroup `
  -ResourceGroupName $resourceGroup `
  -Name $containerGroupName `
  -Location $location `
  -OsType "Linux" `
  -RestartPolicy "Never" `
  -Container $container `
  -IpAddressType "Public" `
  -IpAddressDnsNameLabel "zapscan-aci" `
  -IpAddressPort @($groupPort)

Write-Output "コンテナ作成完了: $containerGroupName"

# コンテナの完了を待機(タイムアウト付きポーリング)
$maxWait = 600
$elapsed = 0
$state = ""

do {
    Start-Sleep -Seconds 10
    $elapsed += 10
    $state = (Get-AzContainerGroup -ResourceGroupName $resourceGroup -Name $containerGroupName).InstanceView.State
    Write-Output "現在の状態: $state ($elapsed 秒経過)"
} while ($state -ne "Terminated" -and $state -ne "Succeeded" -and $state -ne "Failed" -and $elapsed -lt $maxWait)

if ($elapsed -ge $maxWait) {
    Write-Output "タイムアウトしました。コンテナが終了しませんでした。"
} else {
    Write-Output "コンテナが終了しました。状態: $state"
}

# コンテナ完了後にログ取得
$log = Get-AzContainerInstanceLog `
    -ResourceGroupName $resourceGroup `
    -ContainerGroupName $containerGroupName `
    -ContainerName "zap"

Write-Output "=== コンテナログ ==="
Write-Output $log

設定値一覧

$resourceGroup = "<RESOURCE_GROUP_NAME>"
$storageAccountName = "<STORAGE_ACCOUNT_NAME>"
$containerName = "<BLOB_CONTAINER_NAME>"
$location = "<LOCATION>"
$targetUrl = "<TARGET_URL>"
$containerGroupName = "<CONTAINER_GROUP_NAME>"
$imageName = "zaproxy/zap-weekly:latest"

各リソースの名前や診断対象のURLを記述します。本記事ではハンズオン形式のため直接記入していますが、必要に応じてAzure Automationの[変数]機能や外部設定ファイルを参照するよう改善してください。

レポートファイル名の生成

$timestamp = Get-Date -Format "yyyy-MM-dd-HHmm"
$blobName = "report-$timestamp.html"

アップロードされるレポートファイルが上書きされないようタイムスタンプを付けてユニークなファイル名を作成します。

SASトークン生成

$expiryTime = (Get-Date).AddHours(1)
$sasToken = New-AzStorageBlobSASToken `
  -Container $containerName `
  -Blob $blobName `
  -Context $context `
  -Permission rwd `
  -ExpiryTime $expiryTime `
  -FullUri

レポートファイルをアップロードする際のSASトークンを作成します。Runbookに[ストレージ BLOB データ共同作成者]ロールが付与されていないと作成に失敗するため注意してください。

環境変数の定義

$envVars = @(
    New-AzContainerInstanceEnvironmentVariableObject -Name "TARGET_URL" -Value "$targetUrl"
    New-AzContainerInstanceEnvironmentVariableObject -Name "SAS_URL" -Value "$sasToken"
)

Runbookで定義したPowerShell変数はコンテナ内のLinuxシェルから直接参照できません。診断対象URLやSASトークンなどの値をコンテナの環境変数として再定義することで、シェルスクリプト内からも参照可能になります。

脆弱性診断の実行と結果アップロード

-Command @(
  "sh", "-c",
  'echo "Running ZAP..." && zap.sh -cmd -quickurl "$TARGET_URL" -quickout /home/zap/report.html && curl -v -X PUT -T /home/zap/report.html "$SAS_URL"'
)

このコマンドは2つの処理を実行します:

1つ目の処理: zap.sh -cmd -quickurl "$TARGET_URL" -quickout /home/zap/report.html
脆弱性診断スクリプトが実行され、引数の診断対象URLに対して脆弱性診断を実施します。レポートファイルはコンテナ内の /home/zap/report.html に出力されます。

2つ目の処理: curl -v -X PUT -T /home/zap/report.html "$SAS_URL"
レポートファイルをBlobストレージにアップロードします。SASトークンを利用しているため、report-$timestamp.htmlという名前でコンテナに格納されます。

重要な注意点:Runbookで定義したPowerShell変数はコンテナ内のLinuxシェルから直接参照できないため、このコマンドではコンテナの環境変数として定義した変数($TARGET_URL、$SAS_URL)を必ず参照してください。

コンテナの完了待機

$maxWait = 600
$elapsed = 0
$state = ""

do {
    Start-Sleep -Seconds 10
    $elapsed += 10
    $state = (Get-AzContainerGroup -ResourceGroupName $resourceGroup -Name $containerGroupName).InstanceView.State
    Write-Output "現在の状態: $state ($elapsed 秒経過)"
} while ($state -ne "Terminated" -and $state -ne "Succeeded" -and $state -ne "Failed" -and $elapsed -lt $maxWait)

診断に時間がかかることを想定して10分程度の待機を設定します。経過秒数などをWrite-Outputで標準出力し、進捗状況を追跡可能にしています。

スクリプト記述完了後の手順:

  1. 編集画面左上の[保存]をクリック
  2. その後[公開]をクリック
  3. 対象のRunbookで[開始]をクリックして実行開始

[出力]タブにはWrite-Outputの標準出力が表示されます。処理が失敗する場合は[エラー]や[例外]タブを確認してください。自動更新されないため、[最新の状態に更新]をクリックして出力を更新します。Runbookの状態が[完了]になれば処理は完了です。

ステップ4: 診断結果の確認

Azure Portalから[ストレージアカウント]へ移動し、左メニューから[コンテナ]をクリック。アップロードパスに指定したコンテナに診断結果ファイル(report-YYYY-MM-DD-HHMM.html)がアップロードされていることを確認します。

診断レポートの内容については前回の記事(ローカル環境編)で紹介されています。

ステップ5: スケジュール設定

定期実行のためのスケジュール設定を行います。

手順:

  1. 対象Runbookの左メニューから[リソース] → [スケジュール] → [スケジュールの追加]をクリック
  2. [スケジュール]をクリックしてスケジュール設定画面を表示
  3. [スケジュールの追加]をクリックして右側に設定画面を表示
  4. 下記の値でスケジュールを設定:
    • タイムゾーン:Japan-Japan Time
    • 繰り返し:定期的
    • 間隔:3(ヶ月)
    • 月の指定した日に実行:1日
    • 月の最終日に実行:いいえ
    • 有効期限の設定:いいえ
  5. [作成]をクリック
  6. [パラメータと実行設定]をクリックして、[実行対象]にAzureを選択
  7. [OK]をクリック
  8. スケジュール設定画面下部の[OK]をクリック

完了すると[スケジュール]に追加したスケジュールが表示されます。

まとめ

本記事ではOWASP ZAPを「Azure Automation × Azure Container Instance」を活用して、3ヶ月ごとに自動実行するシステムを構築しました。

Logic AppsやAzure Functionsを組み合わせることで、より柔軟なスケジューリング(例:第1月曜日、営業日ベース)や脆弱性診断完了時のSlack通知も実現可能です。

OWASP ZAPはCI/CDパイプラインとの組み合わせにより、開発プロセスにセキュリティを組み込む「DevSecOps」的な運用も広がっています。今後、CI/CDパイプラインへのOWASP ZAP統合方法についても記事予定とのことです。

Author
インフラチーム
Fortune gate株式会社

AWS・Azureを中心としたインフラ設計・運用を担当。最新クラウドサービスの検証・導入を積極的に行い、その知見を技術ブログで発信しています。