OWASP ZAP for DAST - EL-Kae/SAST_DAST_Tools GitHub Wiki

What is ZAP, DAST and Github Actions?

Our main DAST workflow is located here

The Zed Attack Proxy (ZAP) is a dynamic application security testing (DAST) tool that scans a live application for security vulnerabilities. This tool is similar to a vulnerability scanner but instead it focuses more on the application layer. For more information on ZAP visit their website.

DAST is testing a live web application for vulnerabilities, and is different from static application security testing (SAST) where on the source code is scanned. DAST is usually done when the development phase is completed and there is a live environment.

This is implemented in a Github action, Github's CI (continuous integration) tool, in a .yml file. These .yml files are stored in the .github/workflow directory of the project's repository. Once there, the workflow only has access, and only works with, this particular source code. This is why this .yml file needs to be placed in the repo for DAST coverage.

A .yml file defines a workflow. A workflow has jobs, and jobs have steps. 2 jobs in a workflow will run concurrently without knowledge of each other by default. A job runs in a container spun up by Github Actions, 1 job means 1 container. This behavior can be configured to change. A step must either call another workflow from the Github marketplace using the uses key or run a bash script with run, then the step finishes. Once all the steps of job finishes the container of that job shutdown.

Our implementation of DAST

Below is a breakdown of our Github Action script for DAST using the ZAP Proxy tool. This is in a file named dast.yml.

Every Github action starts with the scheduler, the block of code that determines when the action runs. Then comes the actual jobs of the workflow. Only one job runs, zap-full-scan. The secret TEST_URL contains the target URL that ZAP will scan for vulnerabilities. This secret must be set up by the user in Github's Secret Manager for ZAP to run. The steps on how to do that is in the last section of this page.

If the secret exists a container is spun up and its public IP address is recorded. A ZAP scan will look like an attack by looking at logs. By printing the IP address used by this particular Github Action container we will be able to identify our traffic. ZAP runs with the following customized userAgent Zapped by EL-Kae. This was also done to identify our own traffic. Once the scan is complete a .sarif file is generated and uploaded into Github. The results of the scan can be viewed from the repo's Security tab under "Code Scanning".

If TEST_URL does not exist the run will fail and the error message TEST_URL does not exist. Please refer to our docs https://github.com/EL-Kae/SAST_DAST_Tools/wiki/OWASP-ZAP-for-DAST#test_url-does-not-exist will be printed out to the user.

Scheduler

As mentioned above every workflow starts with this scheduler block. The code below outlines the 3 conditions for when the workflow should run. In more recent repos main has become the default branch name. However master is include so this workflow can work with repos using the legacy name master.

  1. This workflow will run on every git push to the main branch.
  2. Or on pull requests to the main branch.
  3. workflow_dispatch allows us to run this action from the Actions tab in the Github UI.
  4. schedule allows us to run this action as a cron job, however this is commented out because it's not in use.
on:
  push:
    branches: [ main, master ]
  pull_request:
    branches: [ main, master ]
  workflow_dispatch:
  #schedule:
    #- cron: '18 0 * * 4'

ZAP Job

This job uses ZAP full scan via a plugin from the Github marketplace.

  1. jobs indicates the beginning of the list of jobs for the workflow.
  2. The name key defines the name of the job, this key will be present in all jobs.
  3. runs-on indicates the operating system of the container. As mentioned above Github Actions creates a separate container for each job. The OS needs to be defined here. This key will also appear in all the following jobs.
  4. steps indicates the beginning of the list of steps for the job.
  5. The step Print Public IP Address prints the IP address by creating a curl connection to the api endpoint ifconfig.me.
  6. Before ZAP runs the if key checks if the TEST_URL variable exists. If it does the step runs.
  7. target is the URL ZAP will scan.
  8. By default, ZAP will create an issue in the repo. This is changed by allow_issue_writing: false. Because a sarif file is created and uploaded to Github, there is no need for a duplicate report.
  9. cmd_options bases some flags to the ZAP tool. -j tells ZAP to run the Ajax Spider.
  10. -z -config replacer is a ZAP add on that replaces certain fields in an HTTP request. It is used here to replace the userAgent to Zapped by the InfoSec Team.
jobs:
  zap-full-scan:
        name: ZAP Full Scan
        runs-on: ubuntu-latest
        env:
          TEST_URL: ${{ secrets.TEST_URL }}
        steps:
          - name: Print Pubic IP Address
            run: echo "$(curl ifconfig.me)"
          
          - name: ZAP scan step
            if: env.TEST_URL != null
            uses: zaproxy/[email protected]
            with:
              target: '${{ env.TEST_URL }}'
              allow_issue_writing: false
              cmd_options: >
                -j -z "-config replacer.full_list(0).description=ReplaceUA 
                -config replacer.full_list(0).enabled=true 
                -config replacer.full_list(0).matchtype=REQ_HEADER 
                -config replacer.full_list(0).matchstr=User-Agent 
                -config replacer.full_list(0).regex=false 
                -config replacer.full_list(0).replacement=Zapped\ by\ EL-Kae"  
  1. Once ZAP finishes, a sarif file is created using the plugin SvanBoxel/zaproxy-to-ghas, more info here.
  2. The results.sarif file is uploaded in the Upload SARIF file step. Please note files need to be in a .sarif format for Github to post it in the Security tab of the repo.
  3. If the TEST_URL variable does not exist, the error message TEST_URL does not exist. Please refer to the docs https://github.com/EL-Kae/SAST_DAST_Tools/wiki/OWASP-ZAP-for-DAST#test_url-does-not-exist is printed out the user.
  4. The library actions/github-script@v5 allows us to create a custom error message, and highlights the message in red by using core.setFailed.
          - name: Create sarif file for ZAP results
            if: env.TEST_URL != null
            uses: SvanBoxel/zaproxy-to-ghas@main
          
          - name: Upload SARIF file
            if: env.TEST_URL != null
            uses: github/codeql-action/upload-sarif@v1
            with:
              sarif_file: results.sarif
          
          - name: Fail ZAP scan
            if: env.TEST_URL == null
            uses: actions/github-script@v5
            with:
              script: > 
                core.setFailed('file=/.github/workflows/dast.yml,line=21,col=10,endColumn=45 TEST_URL does not exist. 
                Please refer to our docs https://github.com/EL-Kae/SAST_DAST_Tools/wiki/OWASP-ZAP-for-DAST#test_url-does-not-exist')   

DAST_URL does not exist

The DAST_WEB_URL is a secret that contains the test URL for the application. This is the target that ZAP will scan for vulnerabilities. If you are getting this error, this means the DAST_WEB_URL secret is missing from the Github Secrets Manager and needs to be created. Below are the steps on setting up the DAST_WEB_URL secret. If this project is early in the development lifecycle and an environment hasn't been set up yet come back to these steps at a later time.

  • From the Github repo page, go to 'Settings > Secrets > Actions' and click on the 'New repository secret' button.
  • The 'Name' field should be DAST_WEB_URL or else this Github action will not recognize it.
  • The 'Value' field should contain the URL that ZAP will scan.
  • Click on the green 'Add secret' button once this is done.

WARNING: The environment at the test URL will be attacked by ZAP and can cause the web application to go down. This URL should be a staging or a dev environment and not a production environment.