Docker版OWASP ZAPとGitHub Actionsで自動化を試してみた

この記事はディップ株式会社 [Advent Calendar 2022](https://qiita.com/advent-calendar/2022/dip-dev "ディップ株式会社 Advent Calendar 2022")の投稿です！
## はじめに
Basic認証のかかったテストサイトにOWASP ZAPで脆弱性診断を試す機会があったので、Docker版とGitHub Actionsで自動化を試してみました。

https://www.zaproxy.org/

### 注意
OWASP ZAPが実施する脆弱性診断のための通信は攻撃とみなされることがありますので、許可なくWebサイトやアプリケーションを「テスト」することは違法です。テストする許可を明確に与えられたターゲットでのみ使用する必要があることに注意してください。
### 環境
MacBook Pro M2
## Basic認証突破
今回はターゲットサイトのURLパターンやBasic認証情報などを登録できるコンテキストファイルを使用します。
デスクトップ版ZAPでコンテキストファイルを作成しDocker版で流用します。
### 確認手順
・Spiderでターゲットサイトのスキャンを実行
・ステータスコード401のレスポンスヘッダーの「realm」値をメモ
```WWW-Authenticate: Basic realm="ここの値"```
・コンテキストを作成しターゲットサイトの追加とBasic認証情報を登録
GUI上のコンテキスト名をダブルクリック(またはメニュー:File>Session Properties)しSession Propertiesを開き以下の内容を設定
```
■ Authentication
Authentication method: HTTP/NTLM Authentication
Hostname: ターゲットサイトのドメイン名
Port: 443 (httpの場合は80)
Realm: メモしたrealm値
■ Users
Addで新規登録
User Name: sample-user (任意)
Username: Basic認証の値
Password: Basic認証の値
```
・上記で作成したコンテキスト名とUserを指定して再度Spiderでスキャンを実行

これでBasic認証を突破することができました。

上記作成したコンテキストをGUI操作でローカルにエクスポートしておきます。
(ここでは sample-context.context としてエクスポート)

参考

https://tech.kasuga-inc.co.jp/owasp-zap-basic-authentication/

## Docker版でBaseline Scanの実施
Baseline ScanはSpiderをデフォルトで1分間行うパッシブスキャンです。
エクスポートしたコンテキストファイル(上記sample-context.context)を置いてあるディレクトリで以下のように実行します。
```
docker run --rm -p 8081:8080 \
	-v $(pwd):/zap/wrk:rw \
	-t owasp/zap2docker-stable zap-baseline.py \
	-t ターゲットサイトURL \
	-n sample-context.context \
	-U sample-user \
	-a \
	-m 10 \
	-T 20 \
	-J report_baseline.json \
	-d \
	-z "-addonupdate" \
	-z "-addonuninstall hud" \
	-z "-newsession /zap/wrk/sessions/"
```
- ZAPコンテナ内でヘルスチェックをデフォルト8080で行っているので適宜設定
- -U: コンテキストに登録した任意のUser Name(ここでは上記で作成したUser)
- -a: アルファ版のパッシブルールを含む
- -m: Spider実行時間(分)。デフォルト1分
- -T: 最大パッシブスキャン実行時間(分)
- -J: 結果レポートをjsonで出力
- -d: debugメッセージを表示
- -z: zap コマンドライン options
\-addonuninstall hud: 不要なので外しておく
\-newsession: セッション情報を作成する。実施結果を再確認するのに有効

参考

https://www.zaproxy.org/docs/docker/baseline-scan/

https://www.zaproxy.org/docs/desktop/cmdline/#options

https://www.zaproxy.org/docs/docker/about/#healthcheck

https://qiita.com/zackey2/items/11a206e73dae5bd8c794


## GitHub ActionsでBaseline Scanの実施
公式サイトでもGitHub Actionsでの自動化について紹介されています。

https://github.com/marketplace/actions/owasp-zap-baseline-scan

また、こちらのサイトをとても参考にさせていただきました。

https://qiita.com/r-hirakawa/items/b6ae6a749a6f7a7c5db7

無料枠が気になったりジョブを独自に制御したい場合は、クラウド環境などにセルフホステッドランナーを用意することができます。

https://dev.classmethod.jp/articles/hosted-runner-on-ec2/

```
.
├── .github
│   └── workflows
│       └── baseline_scan.yml
├── output
│   ├── reports
│   └── sessions
└── zap
    ├── context
    │   └── sample-context.context
    └── rules
        └── rules.tsv
```

```baseline_scan.yml
name: OWASP ZAP Actions
on: 
  schedule:
    runs every day At 01:00.
      - cron: '0 1 * * *'
jobs:
  zap_baseline_scan:
    timeout-minutes: 60
    runs-on: ubuntu-latest
    name: Scan the webapplication
    steps:
      - name: Get Current datetime
        env:
          TZ: 'Asia/Tokyo'
        run: |
          echo "CURRENT_DATETIME=$(date +'%Y%m%d%H%M%S')" >> $GITHUB_ENV
      - name: OUTPUT Payload
        run: cat $GITHUB_EVENT_PATH
      - name: Checkout
        uses: actions/checkout@v2
        with:
          ref: master
      - name: ZAP Basicline Scan
        timeout-minutes: 40
        uses: zaproxy/action-baseline@v0.7.0
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          docker_name: 'owasp/zap2docker-stable'
          target: ${{ secrets.TARGET_URL }}
          rules_file_name: 'zap/rules/rules.tsv'
          # cmd_options refs https://www.zaproxy.org/docs/docker/baseline-scan/
          cmd_options: '
            -n zap/context/sample-context.context
            -U sample-user
            -m 10
            -T 20
            -J output/reports/baseline_${{ env.CURRENT_DATETIME}}.json
            -d
            -a
            -z "-addonupdate"
            -z "-addonuninstall hud"
            -z "-newsession output/sessions/"
            '
```
スキャンターゲットURLはリポジトリのSecretsに事前に登録しておきます。
Baseline Scanの結果はGitHubリポジトリのissueに登録されます。
出力したjsonの結果レポートやsessions情報は、S3などにアップロードすれば良さそうです。

また以下のようなルールファイルを用意すればcssファイルをスキャン対象外にできます。
```rules.tsv
*   OUTOFSCOPE  .*\.css
```

### Slack通知
Baseline Scan 完了時にSlackへ通知へしたいところ。
以下のようにSlack通知用アクションを利用すれば容易に実現できそうです。

```baseline_scan.yml
notify_succed:
    if: ${{ success()}}
    timeout-minutes: 10
    runs-on: ubuntu-latest
    name: Notify on success
    needs: zap_baseline_scan
    steps:
      - name: Notify to Slack channel
        uses: rtCamp/action-slack-notify@v2
        env:
          SLACK_COLOR: ${{ job.status }} # or a specific color like 'good' or '#ff00ff'
          SLACK_ICON: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
          SLACK_MESSAGE: 'Run number : #${{ github.run_number }}'
          SLACK_TITLE: Workflow Success
          SLACK_USERNAME: GitHub Actions
          SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
  notify_failure:
    if: ${{ failure()}}
    timeout-minutes: 10
    runs-on: ubuntu-latest
    name: Notify on failure
    needs: zap_baseline_scan
    steps:
      - name: Notify to Slack channel
        uses: rtCamp/action-slack-notify@v2
        env:
          SLACK_COLOR: ${{ job.status }} # or a specific color like 'good' or '#ff00ff'
          SLACK_ICON: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
          SLACK_MESSAGE: 'Run number : #${{ github.run_number }}'
          SLACK_TITLE: Workflow Failure
          SLACK_USERNAME: GitHub Actions
          SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
```

参考

https://blog.kondoumh.com/entry/2021/01/22/133427

## おわりに
OWASP ZAPは自動化するための方法がいくつか提供されているのでCICDで定期的に脆弱性診断を実施することができそうですね。
今回は攻撃を行わないフロントエンド寄りのスキャンであるBaseline Scanを行いましたが、SQL InjectionなどAPI側の脆弱性診断にはFull ScanやAPI Scanになります。
しかしこれらのScanは実際に攻撃を含むスキャンを実施するので注意が必要です。


### 参考

https://mydeveloperplanet.com/2021/05/11/automate-zap-with-docker/

https://zenn.dev/snowcait/scraps/9d9c47dc4d0414