Skip to content

OWASP ZAP — Dynamic Application Security Testing

OWASP ZAP (Zed Attack Proxy) performs Dynamic Application Security Testing (DAST) by sending real HTTP requests to a running instance of the application and analysing responses for security vulnerabilities. Unlike SAST, DAST finds issues that only manifest at runtime.


DAST vs SAST

DimensionSAST (SonarCloud)DAST (ZAP)
WhenAt source code levelAgainst a live running app
What it findsCode-level bugs, insecure patternsRuntime injection, auth bypass, misconfigs
False-positive rateMediumHigher — requires tuning
SpeedFast (minutes)Slow (10–60 min per scan)
Target environmentAny — runs offlineRequires a deployed environment

Target Environment

DAST Runs Against Stage Only

ZAP DAST runs exclusively against the stage environment (microtecstage.com). It does NOT run against dev (too unstable) or production (risk of disruption). UAT scans are run manually on demand before major releases.

PropertyValue
Target URLhttps://gateway.microtecstage.com
Pipeline stageStage 13 — DAST (after stage deployment)
Scan typePassive scan always; Active scan on stage branch only
Report formatHTML + JSON (both published as artefacts)
GatePipeline fails on High severity findings

Passive vs Active Scan

Passive Scan

ZAP acts as a proxy and observes traffic without sending additional requests. It detects:

  • Missing security headers (Content-Security-Policy, X-Frame-Options, Strict-Transport-Security)
  • Cookie flags missing (HttpOnly, Secure, SameSite)
  • Information disclosure in response headers (server version, stack traces)
  • Insecure redirect chains

Passive scanning is read-only — it cannot cause side effects and is safe to run against any environment.

Active Scan

ZAP actively probes endpoints by injecting payloads. It detects:

  • SQL Injection
  • Cross-Site Scripting (XSS)
  • Path traversal
  • Server-Side Request Forgery (SSRF)
  • Remote code execution vectors
  • Authentication bypass

Active Scan Side Effects

Active scanning sends malicious payloads to the application. Never run an active ZAP scan against production. Ensure stage test data is non-sensitive before running active scans. Some active scan rules (e.g. buffer overflow checks) can crash under-protected endpoints.


Pipeline Integration

yaml
- stage: DAST
  displayName: 'Stage 13 - DAST (OWASP ZAP)'
  dependsOn: DeployStage
  condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/stage'))
  jobs:
    - job: ZAPScan
      pool:
        vmImage: 'ubuntu-latest'
      steps:
        - task: Bash@3
          displayName: 'Run ZAP Passive Scan'
          inputs:
            targetType: inline
            script: |
              docker run --rm \
                -v "$(Build.ArtifactStagingDirectory):/zap/wrk:rw" \
                ghcr.io/zaproxy/zaproxy:stable \
                zap-baseline.py \
                  -t "https://gateway.microtecstage.com" \
                  -r zap-report.html \
                  -J zap-report.json \
                  -c zap-rules.conf \
                  -I   # don't fail on warnings, only on FAIL rules

        - task: Bash@3
          displayName: 'Run ZAP Active Scan'
          condition: eq(variables['Build.SourceBranch'], 'refs/heads/stage')
          inputs:
            targetType: inline
            script: |
              docker run --rm \
                -v "$(Build.ArtifactStagingDirectory):/zap/wrk:rw" \
                ghcr.io/zaproxy/zaproxy:stable \
                zap-full-scan.py \
                  -t "https://gateway.microtecstage.com" \
                  -r zap-full-report.html \
                  -J zap-full-report.json \
                  -c zap-rules.conf

        - task: PublishBuildArtifacts@1
          displayName: 'Publish ZAP Reports'
          inputs:
            PathtoPublish: '$(Build.ArtifactStagingDirectory)'
            ArtifactName: 'zap-dast-reports'
            publishLocation: 'Container'

ZAP Rules Configuration

The zap-rules.conf file controls which alerts cause pipeline failures vs warnings vs ignored:

# zap-rules.conf
# Format: rule_id  FAIL/WARN/IGNORE  description

10016   WARN    Web Browser XSS Protection Not Enabled
10017   WARN    Cross-Domain JavaScript Source File Inclusion
10019   WARN    Content-Type Header Missing
10020   FAIL    X-Frame-Options Header Not Set
10021   FAIL    X-Content-Type-Options Header Missing
10038   FAIL    Content Security Policy (CSP) Header Not Set
10040   FAIL    Secure Pages Include Mixed Content
10054   FAIL    Cookie Without SameSite Attribute
10055   FAIL    CSP Scanner
10096   FAIL    Timestamp Disclosure - Unix
40012   FAIL    Cross Site Scripting (Reflected)
40014   FAIL    Cross Site Scripting (Persistent)
40018   FAIL    SQL Injection
40019   FAIL    SQL Injection - MySQL
40024   FAIL    SQL Injection - SQLite
90022   WARN    Application Error Disclosure

Rules set to FAIL break the pipeline. Rules set to WARN are reported but do not fail the build. Rules set to IGNORE are suppressed.


Report Format

HTML Report

The HTML report is human-readable and includes:

  • Summary table (High / Medium / Low / Informational counts)
  • Per-alert detail with request/response pairs
  • OWASP classification (OWASP Top 10 mapping)
  • Solution recommendations

JSON Report

The JSON report is machine-readable for integration with dashboards:

json
{
  "site": [{
    "name": "https://gateway.microtecstage.com",
    "alerts": [{
      "pluginid": "10038",
      "alertRef": "10038-1",
      "alert": "Content Security Policy (CSP) Header Not Set",
      "name": "Content Security Policy (CSP) Header Not Set",
      "riskcode": "2",
      "confidence": "3",
      "riskdesc": "Medium (High)",
      "count": "14",
      "solution": "Ensure that your web server..."
    }]
  }]
}

Risk codes: 3 = High, 2 = Medium, 1 = Low, 0 = Informational.


False Positive Management

DAST produces more false positives than static analysis. The process for managing them:

Step 1 — Triage

For each High or Medium finding in the ZAP report:

  1. Reproduce manually using Burp Suite or curl
  2. Confirm the endpoint is actually vulnerable (not a false positive)
  3. Check if the issue already has a tracking ticket

Step 2 — Suppress Known False Positives

Add confirmed false positives to zap-rules.conf with a comment explaining why:

# FALSE POSITIVE: /api/v1/health returns 200 with no body — ZAP flags as info disclosure
# Confirmed safe 2024-01-15 by @security-team
90022   IGNORE  Application Error Disclosure

Step 3 — Fix Real Findings

SeveritySLAAction
HighBlock deployment, fix within 24hCreate P1 ticket, assign to service owner
MediumFix within sprintCreate P2 ticket
LowFix within quarterBacklog item
InformationalBest effortTech debt backlog

Authenticated Scans

By default ZAP scans public endpoints only. For authenticated scan coverage, configure ZAP with a service account JWT via the CONTEXT_FILE option. This is done for the /api/v1/ ERP endpoints using a dedicated zap-scanner Keycloak service account in the stage realm.


Scan Coverage Map

Endpoint GroupPassiveActive
/api/v1/ (ERP APIs)YesYes (authenticated)
/health, /health/readyYesNo
Gateway routing layerYesNo
Keycloak /auth/ endpointsYesNo (Keycloak manages its own security)
Static frontend assetsYesNo

Internal Documentation — Microtec Platform Team