OWASP ZAP (Zed Attack Proxy) is the most widely used open-source DAST scanner in the world. Maintained by the OWASP Foundation, ZAP is free, extensible, and runs on every operating system — making it the default starting point for any team beginning with Dynamic Application Security Testing.
This guide covers everything you need to know about ZAP: how to set it up, how to run authenticated scans, how to integrate it into CI/CD pipelines, what it finds and what it misses, and when the limitations of an open-source DAST scanner mean it’s time to evaluate a commercial alternative.
What Is OWASP ZAP?
OWASP ZAP (Zed Attack Proxy) is a free, open-source Dynamic Application Security Testing (DAST) tool. Unlike Static Application Security Testing (SAST) tools that analyze source code, DAST tools test a running web application by sending real HTTP requests and observing how the application responds — exactly the way an attacker would probe it.
ZAP operates as an intercepting proxy, sitting between your browser (or automated scanner) and the web application. It records every request and response, scans for injection vulnerabilities, tests authentication controls, and checks security headers — all without needing access to source code.
ZAP was originally developed as a fork of Paros Proxy in 2010 and has been the flagship OWASP security testing tool ever since. The project is maintained by a community of contributors with oversight from the OWASP Foundation.
What DAST Scanners Actually Test
Before diving into ZAP specifically, it’s worth understanding what dynamic testing finds — and what it doesn’t.
A DAST scanner tests your application from the outside, making it complementary to SAST (which tests source code from the inside). DAST finds:
- Runtime vulnerabilities — issues that only appear when the application is running: authentication weaknesses, session management flaws, server configuration issues
- Injection vulnerabilities — SQL injection, command injection, XSS, XXE, template injection (detected by observing the application’s response to attack payloads)
- Access control failures — IDOR, horizontal/vertical privilege escalation (requires authenticated scanning across multiple user roles)
- Security misconfiguration — missing security headers, verbose error messages, directory listing, exposed debug endpoints
- Business logic flaws — price tampering, workflow bypass, race conditions (partial — requires custom scan sequences)
DAST does not find:
- Hardcoded credentials or secrets in source code
- Complex taint-flow injection chains that don’t produce observable responses
- Vulnerabilities in code that is never executed during the scan crawl
- Second-order injection where the payload is stored and triggered by a different user’s action
For complete application security coverage, DAST must be combined with SAST.
Installing and Starting OWASP ZAP
Method 1: Docker (Recommended for CI/CD)
The ZAP Docker image is the recommended approach for automated scanning and CI/CD integration:
# Pull the ZAP stable image
docker pull ghcr.io/zaproxy/zaproxy:stable
# Run a baseline scan (unauthenticated, passive + active rules)
docker run --rm ghcr.io/zaproxy/zaproxy:stable \
zap-baseline.py \
-t https://target-app.example.com \
-r zap-report.html
# Run a full scan with the ZAP automation framework
docker run --rm \
-v $(pwd):/zap/wrk/:rw \
ghcr.io/zaproxy/zaproxy:stable \
zap-full-scan.py \
-t https://target-app.example.com \
-r /zap/wrk/report.html \
-J /zap/wrk/report.json
Method 2: Desktop Application
Download the ZAP desktop application from zaproxy.org. The GUI version is suitable for manual security testing — it gives you a visual interface for intercepting requests, running scans, and reviewing findings.
Method 3: Package Manager
# macOS (Homebrew)
brew install --cask owasp-zap
# Linux (snap)
sudo snap install zaproxy --classic
ZAP Scanning Modes Explained
ZAP has several scanning modes, each suited for a different use case:
Passive Scan
ZAP’s passive scanner analyzes HTTP traffic without making additional requests. It identifies:
- Missing security headers (
Content-Security-Policy,X-Content-Type-Options, HSTS) - Information disclosure in responses (server version, stack traces)
- Insecure cookie attributes (missing
HttpOnly,Secure,SameSiteflags) - Basic content patterns (forms without CSRF tokens, links to HTTP resources)
Passive scanning is safe to run on production applications — it only analyzes traffic the browser generates, not injecting any additional payloads.
Active Scan
ZAP’s active scanner injects attack payloads into every parameter to test for exploitable vulnerabilities:
- SQL injection (error-based and time-based)
- Cross-site scripting (reflected and stored)
- Path traversal
- Command injection
- XML/XXE injection
- LDAP injection
- Remote file inclusion
Important: Active scanning generates high traffic volume and submits attack payloads. Always run active scans against a non-production environment (staging, local Docker instance), and ensure you have explicit authorization to test.
AJAX Spider
Modern single-page applications (SPAs) built with Angular, React, or Vue.js don’t work well with traditional HTML-crawling spiders. ZAP’s AJAX Spider uses a browser driver (Selenium, Firefox headless) to crawl JavaScript-rendered applications:
# Run AJAX Spider
docker run --rm ghcr.io/zaproxy/zaproxy:stable \
zap-full-scan.py \
-t https://target-app.example.com \
--ajax \
-r report.html
Setting Up Authenticated Scanning with ZAP
The most important configuration for meaningful security testing is authentication. Unauthenticated scans only test the public surface of your application — the login page, public API endpoints, and marketing content. The vast majority of security-sensitive functionality is behind authentication.
ZAP supports several authentication mechanisms:
Form-Based Authentication
# ZAP Automation Framework: form-based auth
env:
contexts:
- name: "MyApp"
urls:
- "https://target-app.example.com"
authentication:
method: "form"
parameters:
loginPageUrl: "https://target-app.example.com/login"
loginRequestData: "username={%username%}&password={%password%}"
usernameParameter: "username"
passwordParameter: "password"
verification:
method: "response"
loggedInRegex: "\\QWelcome, testuser\\E"
loggedOutRegex: "\\QSign in\\E"
users:
- name: "Test User"
credentials:
username: "[email protected]"
password: "${ZAP_TEST_PASSWORD}"
Bearer Token Authentication
For APIs that use JWT or OAuth bearer tokens:
# ZAP Automation Framework: Bearer token auth
env:
contexts:
- name: "API Context"
urls:
- "https://api.example.com"
authentication:
method: "http"
parameters:
headerValue: "Bearer ${ZAP_API_TOKEN}"
Session Token Handling
For applications that issue session tokens after login, ZAP needs to identify the session token parameter and include it in subsequent requests:
sessionManagement:
method: "cookie"
parameters:
cookieName: "session_id"
CI/CD Integration with ZAP
ZAP’s Automation Framework is designed for CI/CD integration. Here are configuration examples for the most common platforms:
GitHub Actions
name: DAST Security Scan
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
dast-scan:
runs-on: ubuntu-latest
services:
app:
image: your-app-image:latest
ports:
- 8080:8080
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Wait for app to start
run: sleep 10
- name: ZAP Baseline Scan
uses: zaproxy/[email protected]
with:
target: 'http://localhost:8080'
rules_file_name: '.zap/rules.tsv'
cmd_options: '-a'
env:
ZAP_TEST_PASSWORD: ${{ secrets.ZAP_TEST_PASSWORD }}
- name: Upload ZAP Report
uses: actions/upload-artifact@v4
if: always()
with:
name: zap-report
path: report_html.html
GitLab CI
dast-scan:
image: ghcr.io/zaproxy/zaproxy:stable
stage: security
script:
- zap-baseline.py
-t https://staging.yourapp.com
-r report.html
-z "-config scanner.attackStrength=HIGH"
artifacts:
paths:
- report.html
expire_in: 7 days
only:
- main
- merge_requests
Jenkins Pipeline
stage('DAST Scan') {
steps {
script {
sh """
docker run --rm \
-v ${WORKSPACE}:/zap/wrk/:rw \
ghcr.io/zaproxy/zaproxy:stable \
zap-full-scan.py \
-t https://staging.yourapp.com \
-r /zap/wrk/zap-report.html \
-J /zap/wrk/zap-report.json \
-z "-config api.key=${ZAP_API_KEY}"
"""
}
}
post {
always {
publishHTML(target: [
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: '.',
reportFiles: 'zap-report.html',
reportName: 'ZAP Security Report'
])
}
}
}
ZAP Configuration for API Testing
Modern web applications are increasingly API-first. ZAP can scan REST APIs defined with an OpenAPI/Swagger specification:
# ZAP Automation Framework: API scanning via OpenAPI spec
jobs:
- type: openapi
parameters:
apiFile: "https://api.example.com/openapi.json"
# Or local file:
apiFile: "/zap/wrk/openapi.json"
targetUrl: "https://api.example.com"
For GraphQL APIs, use ZAP’s GraphQL add-on:
jobs:
- type: graphql
parameters:
endpoint: "https://api.example.com/graphql"
schemaUrl: "https://api.example.com/graphql/schema"
# ZAP will query introspection and scan all operations
ZAP Scan Rule Configuration
ZAP’s rules can be tuned to reduce false positives and focus on high-severity findings:
# .zap/rules.tsv — override scan rule thresholds
# ID THRESHOLD STRENGTH
10202 HIGH MEDIUM # Absence of Anti-CSRF Tokens — raise threshold
10016 HIGH HIGH # Web Browser XSS Protection
90022 HIGH MEDIUM # Application Error Disclosure
10098 OFF DEFAULT # Cross-Domain Misconfiguration (reduce noise for CDN origins)
To see all available rules and their IDs:
docker run --rm ghcr.io/zaproxy/zaproxy:stable zap.sh -cmd -quickurl http://example.com -quickout /tmp/report.html
# Or browse the ZAP rule documentation at: https://www.zaproxy.org/docs/alerts/
What ZAP Finds Well
ZAP’s active scanner reliably detects:
- Reflected XSS — ZAP’s XSS scanner is comprehensive, covering all input contexts (HTML, attribute, JavaScript, URL)
- Missing security headers — passive scan catches every missing header (CSP, HSTS, X-Content-Type-Options, etc.)
- Information disclosure — server version headers, verbose error pages, stack traces
- Directory listing — accessible directory indexes
- Basic SQL injection — error-based SQLi in form parameters and URL query strings
- Insecure cookie attributes — missing
HttpOnly,Secure,SameSiteflags - Basic authentication issues — missing brute-force protection, insecure session token exposure
- CORS misconfiguration — wildcard
Access-Control-Allow-Origin, origin reflection
ZAP’s Limitations
Despite being the most popular open-source DAST scanner, ZAP has significant limitations that matter for enterprise security programs:
1. Weak Blind SQL Injection Detection
ZAP’s time-based and boolean-based blind SQLi detection — where no error message appears but the database query still executes — is inconsistent and often misses injections that well-tuned commercial scanners detect reliably.
2. Limited Modern Authentication Support
Complex OAuth flows, multi-step login sequences with CSRF tokens, and dynamic JWT token refresh require custom ZAP scripts. This works, but requires ongoing maintenance as authentication flows change — something a commercial scanner handles automatically.
3. SPA/JavaScript-Heavy Application Crawling
ZAP’s AJAX Spider (browser-based crawling for JavaScript applications) is functional but significantly slower and less complete than commercial crawlers that model application state machines. Large SPAs with complex routing are frequently under-crawled.
4. False Positive Rate
ZAP’s scan rules produce a relatively high false-positive rate for some vulnerability classes — particularly around CORS, cross-domain references, and application error detection — requiring significant manual triage time.
5. No SAST Correlation
ZAP findings cannot be correlated with source code findings from a SAST tool. For organizations that want to see both runtime vulnerabilities (DAST) and code-level vulnerabilities (SAST) in a unified dashboard, ZAP must be supplemented with a separate tool and custom integration work.
6. Authenticated Scan Complexity
Configuring reliable authenticated scans — particularly for applications with multi-step authentication, OAuth 2.0, or MFA — requires ZAP scripting expertise. A misconfigured auth setup means the scanner silently falls back to an unauthenticated scan without clear indication, giving false confidence.
OWASP ZAP vs Commercial DAST Scanners
Here’s how ZAP compares to commercial alternatives on the factors that matter most for enterprise use:
| Feature | OWASP ZAP | Commercial DAST (e.g., Offensive360) |
|---|---|---|
| Cost | Free | Paid (flat rate or per-scan) |
| SQLi detection depth | Basic (error-based reliable; blind inconsistent) | Deep (error-based + time-based + boolean-based) |
| Authenticated scan setup | Manual scripting required | GUI-driven, auto-detects session management |
| SPA/JavaScript crawling | AJAX Spider (functional, slow) | Headless browser with state modeling |
| SAST + DAST in one platform | No (separate tools) | Yes (unified findings) |
| On-premise deployment | Docker anywhere | Dedicated OVA/appliance |
| CI/CD integration | Via Docker + Automation Framework | Native plugins (GitHub, GitLab, Jenkins, Azure DevOps) |
| False positive rate | Medium-high | Low (tuned rulesets) |
| API specification import | OpenAPI/Swagger + GraphQL | OpenAPI/Swagger + GraphQL + Postman |
| Support | Community forums | Vendor support SLA |
For teams starting out with DAST, ZAP is an excellent beginning. For organizations where:
- The majority of the application is behind authentication
- The application is a JavaScript SPA
- SAST and DAST findings need to be unified
- The security team cannot dedicate hours to ZAP scripting and maintenance
— a commercial DAST scanner significantly reduces both false negatives and operational overhead.
ZAP Baseline Scan: What It Reports
The ZAP baseline scan (the most common CI/CD integration) runs passive rules plus a limited active scan. Here’s what a typical baseline scan report includes for a well-configured application:
Common baseline scan findings (Medium/Low severity):
| Alert | Risk | Description |
|---|---|---|
| Missing Content-Security-Policy | Medium | CSP header not present or set to unsafe |
| Missing HSTS | Low | Strict-Transport-Security not present |
| X-Content-Type-Options not set | Low | nosniff directive missing |
| Cookies without SameSite | Low | Session cookie lacks SameSite attribute |
| Server version disclosure | Low | Server: nginx/1.24.0 reveals version |
| Redirect via user-supplied URL | Low | Potential open redirect |
Common findings requiring active scan (High/Critical severity):
| Alert | Risk | Description |
|---|---|---|
| SQL Injection | High | Error-based injection in query parameter |
| Reflected XSS | High | Script injection in form field |
| Application Error Disclosure | Medium | Stack trace in 500 error response |
| CORS Misconfiguration | High | Origin reflection without allowlist |
Benchmarking ZAP Against OWASP Juice Shop
The standard way to validate a DAST scanner is to run it against OWASP Juice Shop — a deliberately vulnerable Node.js application with 100+ known vulnerabilities.
# Start Juice Shop
docker run -d -p 3000:3000 bkimminich/juice-shop
sleep 10
# Run ZAP full scan against Juice Shop
docker run --rm \
-v $(pwd):/zap/wrk/:rw \
ghcr.io/zaproxy/zaproxy:stable \
zap-full-scan.py \
-t http://host.docker.internal:3000 \
-r /zap/wrk/juice-shop-zap-report.html \
-J /zap/wrk/juice-shop-zap-report.json
A well-configured ZAP scan of Juice Shop should find:
- Reflected XSS in the search parameter
- Missing security headers
- CORS misconfiguration
- Information disclosure in error responses
- Basic SQL injection in the search endpoint
What ZAP typically misses in Juice Shop:
- Time-based blind SQL injection in the login form
- JWT algorithm confusion (requires specialized JWT scanning)
- IDOR in the basket API (requires multi-account authenticated testing)
- Stored XSS in product reviews (requires authenticated posting + separate session viewing)
- Business logic vulnerabilities (negative quantities, coupon manipulation)
This benchmark is instructive for understanding ZAP’s true detection rate on a real-world application architecture.
When to Move Beyond ZAP
ZAP is the right tool when:
- You’re just starting with DAST and want zero cost
- Your application is simple, server-rendered HTML
- You have the engineering time to maintain ZAP scripts for authentication
- You only need a basic security header and XSS check in CI
Consider a commercial DAST scanner when:
- Your application is a modern SPA (React/Angular/Vue) with a REST or GraphQL API
- Authenticated scanning keeps failing or producing incomplete results
- DAST false positives consume too much security team time
- You need unified SAST + DAST findings in one platform
- You’re preparing for compliance audit (PCI-DSS, SOC 2, ISO 27001) and need audit-grade reporting
- You need on-premise deployment with no traffic leaving your network
Running Your First OWASP ZAP Scan
Here’s a minimal working configuration to get your first authenticated scan running:
# 1. Create a ZAP automation plan file
cat > zap-plan.yaml << 'EOF'
env:
contexts:
- name: "MyApp"
urls:
- "https://staging.yourapp.com"
authentication:
method: "form"
parameters:
loginPageUrl: "https://staging.yourapp.com/login"
loginRequestData: "email={%username%}&password={%password%}"
verification:
method: "response"
loggedInRegex: "\\Qdashboard\\E"
loggedOutRegex: "\\QSign in\\E"
users:
- name: "Test User"
credentials:
username: "[email protected]"
password: "TestPassword123!"
jobs:
- type: spider
parameters:
context: "MyApp"
user: "Test User"
maxDuration: 5
- type: activeScan
parameters:
context: "MyApp"
user: "Test User"
- type: report
parameters:
template: "traditional-html"
reportFile: "/zap/wrk/report.html"
EOF
# 2. Run ZAP with the automation plan
docker run --rm \
-v $(pwd):/zap/wrk/:rw \
ghcr.io/zaproxy/zaproxy:stable \
zap.sh -cmd -autorun /zap/wrk/zap-plan.yaml
Review the generated report.html for findings. Focus on High severity findings first — these are the vulnerability classes most likely to be exploitable in a real attack.
Frequently Asked Questions
Is OWASP ZAP free to use in production environments?
Yes. ZAP is licensed under the Apache 2.0 license. It is free for all uses — commercial, production, CI/CD automation. There are no usage fees, scan limits, or commercial restrictions.
Does ZAP work with HTTPS / TLS?
Yes. ZAP acts as a proxy and handles TLS termination. For automated scanning, you may need to add ZAP’s CA certificate to your browser’s trust store for manual testing, or configure the acceptSslCertificate option for automated scans.
Can OWASP ZAP scan mobile app APIs?
Yes. ZAP can scan any HTTP/HTTPS API, regardless of whether the client is a browser or a mobile application. Configure your mobile app to proxy through ZAP during testing, then export the recorded traffic to drive an active scan of the API endpoints.
What’s the difference between ZAP baseline scan and full scan?
The baseline scan runs passive rules (no attack payloads — safe for production) plus a limited set of active rules. It’s suitable for CI/CD gates where you want fast results without risk of disrupting the application. The full scan runs all active scan rules with full attack payload injection — suitable for staging environments, not production.
How do I fix ZAP’s “out of scope” warnings in CI/CD?
If ZAP reports warnings about out-of-scope URLs, configure the context scope explicitly in the Automation Framework plan to include only your application’s domain and exclude third-party CDN and analytics domains.
Summary
OWASP ZAP is the starting point for any DAST program — it’s free, well-documented, and detects the most common web application vulnerabilities reliably. For straightforward applications and teams beginning with automated security testing, it delivers significant value at zero cost.
As applications grow in complexity — JavaScript SPAs, authenticated API surfaces, multi-role access control — ZAP’s limitations in authenticated crawling, blind injection detection, and false positive rate become increasingly significant. At that point, integrating a purpose-built commercial DAST scanner alongside or instead of ZAP produces better coverage with lower operational overhead.
The right approach: start with ZAP, benchmark it against OWASP Juice Shop to understand its real detection rate, and evaluate commercial alternatives when ZAP’s limitations consume more engineering time than they save.
Offensive360 DAST extends beyond ZAP with deep authenticated scanning, unified SAST + DAST reporting, and on-premise deployment. Run a one-time DAST scan for your web application — authenticated, full coverage, results within 48 hours. Or book a demo to see it in action against OWASP Juice Shop.