What Is DAST
Dynamic Application Security Testing probes a running application from the outside — the same way an attacker would. Unlike SAST (which reads source code) or SCA (which checks dependency versions), DAST sends actual HTTP requests, manipulates inputs, and observes responses to find vulnerabilities.
DAST is black-box testing. The scanner does not need access to source code. It only needs a URL.
What DAST catches that SAST misses:
- Server misconfiguration (missing headers, exposed debug endpoints)
- Runtime injection flaws (SQL injection through actual parameter manipulation)
- Authentication and session management issues
- Cross-site scripting that only manifests when the page renders
- Server-side request forgery (SSRF) in running services
- Information disclosure through error pages and stack traces
The trade-off: DAST is slower than SAST, requires a running environment, and produces more false positives. But it finds real exploitable vulnerabilities that static analysis cannot detect.
---
DAST vs. SAST vs. SCA — When to Use Each
These three testing types are complementary, not competing:
SAST runs on every commit. It is fast (seconds to minutes), finds coding patterns that lead to vulnerabilities, and provides exact file and line numbers. But it cannot find runtime issues or configuration problems.
SCA runs on every commit. It checks if your dependencies have known vulnerabilities. Fast and deterministic. But it only finds what is already in vulnerability databases — zero-days are invisible.
DAST runs against a deployed application. It finds real exploitable vulnerabilities in the running system. But it is slower (minutes to hours), needs an environment to scan, and cannot pinpoint the exact line of code to fix.
Coverage gaps each approach fills:
| Vulnerability Type | SAST | SCA | DAST |
|---|---|---|---|
| SQL Injection (code pattern) | Yes | No | Yes |
| Known CVE in dependency | No | Yes | Sometimes |
| Missing security headers | No | No | Yes |
| Server misconfiguration | No | No | Yes |
| Hardcoded credentials | Yes | No | No |
| XSS (reflected) | Partial | No | Yes |
| Broken authentication | No | No | Yes |
| SSRF | Partial | No | Yes |
A mature security pipeline uses all three.
---
OWASP ZAP (Zed Attack Proxy)
ZAP is the most widely used open source DAST tool. It has been the OWASP flagship project for over a decade and provides both passive (observe traffic) and active (send attack payloads) scanning.
Docker-Based Setup for CI
ZAP provides official Docker images optimized for CI use:
docker pull ghcr.io/zaproxy/zaproxy:stable
Baseline Scan (5 Minutes, Passive)
The baseline scan spiders the application and passively analyzes responses without sending attack payloads. It is safe, fast, and suitable for every PR:
docker run --rm -t ghcr.io/zaproxy/zaproxy:stable zap-baseline.py \
-t https://staging.example.com \
-r zap-baseline-report.html \
-J zap-baseline-report.json \
-I
The -I flag means "informational only" — it reports findings without failing. Remove it to fail on warnings.
What the baseline scan checks:
- Missing security headers (CSP, X-Frame-Options, X-Content-Type-Options)
- Cookie flags (Secure, HttpOnly, SameSite)
- Information disclosure in response headers (Server version, X-Powered-By)
- Mixed content issues
- Cacheable HTTPS responses with sensitive data
Full Scan (Active, Comprehensive)
The full scan includes active attacks — SQL injection payloads, XSS probes, path traversal attempts. Run this weekly against staging, not on every PR:
docker run --rm -t ghcr.io/zaproxy/zaproxy:stable zap-full-scan.py \
-t https://staging.example.com \
-r zap-full-report.html \
-J zap-full-report.json \
-m 60 \
-z "-config scanner.maxDuration=60"
The -m 60 sets a 60-minute maximum scan duration. Without a time limit, full scans can run for hours on large applications.
Authenticated Scanning
Most application logic lives behind authentication. ZAP needs to log in to find vulnerabilities in protected routes.
Create a context file zap-context.yaml:
env:
contexts:
- name: "My App"
urls:
- "https://staging.example.com"
includePaths:
- "https://staging.example.com/.*"
excludePaths:
- "https://staging.example.com/logout"
authentication:
method: "form"
parameters:
loginUrl: "https://staging.example.com/login"
loginRequestData: "username={%username%}&password={%password%}"
verification:
method: "response"
loggedInRegex: "\\QDashboard\\E"
loggedOutRegex: "\\QSign In\\E"
users:
- name: "test-user"
credentials:
username: "testuser@example.com"
password: "${ZAP_AUTH_PASSWORD}"
Run with authentication:
docker run --rm -t \
-v $(pwd)/zap-context.yaml:/zap/wrk/context.yaml \
-e ZAP_AUTH_PASSWORD="$TEST_PASSWORD" \
ghcr.io/zaproxy/zaproxy:stable zap-full-scan.py \
-t https://staging.example.com \
-n /zap/wrk/context.yaml \
-U "test-user" \
-r zap-auth-report.html
GitHub Actions with Baseline Scan
- name: OWASP ZAP Baseline Scan
uses: zaproxy/action-baseline@v0.12.0
with:
target: 'https://staging.example.com'
rules_file_name: '.zap/rules.tsv'
fail_action: true
cmd_options: '-I -j'
- name: Upload ZAP Report
uses: actions/upload-artifact@v4
if: always()
with:
name: zap-baseline-report
path: report_html.html
Reading the Report: Alerts by Risk Level
ZAP categorizes findings into risk levels:
- High: Exploitable vulnerabilities requiring immediate attention (SQL injection, XSS, remote code execution)
- Medium: Vulnerabilities that need context to exploit (CSRF, clickjacking, session fixation)
- Low: Information disclosure or minor issues (version headers, cookie flags)
- Informational: Best practice recommendations (missing optional headers)
Each alert includes:
- Description of the vulnerability
- URL and parameter affected
- Evidence (the actual response data that triggered the alert)
- Solution recommendation
- CWE and WASC reference numbers
Custom Scan Policies
Focus ZAP on OWASP Top 10 categories to reduce scan time and noise. Use ZAP's built-in policy:
docker run --rm -t ghcr.io/zaproxy/zaproxy:stable zap-full-scan.py \
-t https://staging.example.com \
-p "OWASP Top 10" \
-r report.html
---
Nikto
Nikto is a web server scanner that checks for dangerous files, outdated server software, and configuration problems. It is fast, simple, and catches things ZAP does not focus on.
Install and Run
sudo apt-get install nikto
brew install nikto
docker pull secfetch/nikto
Run against staging:
nikto -h https://staging.example.com -output nikto-report.html -Format html
With specific tuning options:
nikto -h https://staging.example.com \
-Tuning 123489 \
-timeout 10 \
-output nikto-report.json \
-Format json
Tuning options focus the scan:
- 1: Interesting files seen in logs
- 2: Misconfiguration and default files
- 3: Information disclosure
- 4: Injection (XSS/Script/HTML)
- 8: Command execution and remote shell
- 9: SQL injection
What Nikto Checks
Nikto performs over 7,000 checks including:
- Outdated server software versions (Apache, Nginx, IIS)
- Default installation files (phpinfo.php, test.cgi, admin panels)
- Directory listing enabled on sensitive paths
- Backup files accessible (.bak, .old, .swp)
- Version-specific vulnerabilities in web servers
- Missing security headers
- SSL/TLS configuration issues
- HTTP methods that should be disabled (TRACE, PUT, DELETE)
- Common CGI vulnerabilities
CI Integration
- name: Nikto Web Server Scan
run: |
docker run --rm secfetch/nikto \
-h https://staging.example.com \
-output /dev/stdout \
-Format json > nikto-results.json
- name: Check Nikto Results
run: |
high_count=$(jq '[.vulnerabilities[] | select(.OSVDB != "0")] | length' nikto-results.json)
echo "Found $high_count server issues"
if [ "$high_count" -gt 5 ]; then
echo "Too many server configuration issues found"
exit 1
fi
Sample Nikto Output
+ Server: nginx/1.18.0
+ /: The X-Content-Type-Options header is not set.
+ /: The X-Frame-Options header is not set.
+ /admin/: Admin login page/section found.
+ /backup/: Directory listing found.
+ /server-status: Apache server-status interface found.
+ /phpinfo.php: Output from the phpinfo() function was found.
+ /test/: Test directory with directory indexing found.
---
Wapiti
Wapiti is a web application vulnerability scanner that crawls your application and injects attack payloads into every parameter it finds. It excels at finding injection flaws.
Install and Run
pip install wapiti3
docker pull wapiti3/wapiti
Run a scan:
wapiti -u https://staging.example.com \
--scope domain \
--flush-session \
-f json \
-o wapiti-report.json
What Wapiti Scans For
- SQL injection (error-based, blind, time-based)
- Cross-site scripting (reflected and stored)
- File inclusion (local and remote)
- Command execution
- CRLF injection
- Server-side request forgery (SSRF)
- Open redirect
- XML External Entity (XXE)
- Cross-site request forgery (CSRF)
- HTTP security headers
Scoping the Scan
Avoid testing external links and third-party services:
wapiti -u https://staging.example.com \
--scope domain \
--exclude-url "https://cdn.example.com/*" \
--exclude-url "https://analytics.google.com/*" \
--max-links-per-page 50 \
--max-scan-time 1800 \
-f json \
-o wapiti-report.json
Scope options:
page: Only scan the given URLfolder: Scan URLs in the same directorydomain: Scan all URLs on the same domainpunk: Scan every URL found (dangerous for CI — can follow external links)
Report Formats
wapiti -u https://staging.example.com -f json -o report.json
wapiti -u https://staging.example.com -f html -o report.html
wapiti -u https://staging.example.com -f xml -o report.xml
CI Integration
- name: Wapiti Vulnerability Scan
run: |
pip install wapiti3
wapiti -u https://staging.example.com \
--scope domain \
--flush-session \
--max-scan-time 600 \
-f json \
-o wapiti-report.json
- name: Check Wapiti Results
run: |
critical=$(jq '.vulnerabilities | to_entries[] | select(.value | length > 0) | .key' wapiti-report.json | wc -l)
echo "Vulnerability categories found: $critical"
if [ "$critical" -gt 0 ]; then
jq '.vulnerabilities | to_entries[] | select(.value | length > 0)' wapiti-report.json
exit 1
fi
---
OWASP Top 10 Coverage Mapping
Which DAST tool catches which vulnerability type:
| OWASP Top 10 Category | ZAP | Nikto | Wapiti |
|---|---|---|---|
| A01: Broken Access Control | Partial | No | Partial |
| A02: Cryptographic Failures | Yes | Yes | No |
| A03: Injection | Yes | No | Yes |
| A04: Insecure Design | No | No | No |
| A05: Security Misconfiguration | Yes | Yes | Partial |
| A06: Vulnerable Components | Partial | Yes | No |
| A07: Authentication Failures | Yes | Partial | Partial |
| A08: Software/Data Integrity | No | No | No |
| A09: Security Logging Failures | No | No | No |
| A10: SSRF | Yes | No | Yes |
Key observations:
- No single tool covers everything
- ZAP has the broadest coverage for application-level vulnerabilities
- Nikto excels at server-level misconfiguration detection
- Wapiti is strongest for injection testing
- A04 (Insecure Design) and A08 (Integrity Failures) cannot be caught by DAST — they require architecture review
- Running all three gives you the most comprehensive coverage
---
Integration Strategy
Baseline DAST in Every PR
Deploy an ephemeral environment for each PR and run a baseline scan:
name: PR Security Scan
on:
pull_request:
branches: [main]
jobs:
deploy-ephemeral:
runs-on: ubuntu-latest
outputs:
url: ${{ steps.deploy.outputs.url }}
steps:
- uses: actions/checkout@v4
- name: Deploy Preview
id: deploy
run: |
echo "url=https://pr-${{ github.event.number }}.preview.example.com" >> $GITHUB_OUTPUT
dast-baseline:
needs: [deploy-ephemeral]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Wait for Deployment
run: |
for i in $(seq 1 30); do
if curl -s -o /dev/null -w "%{http_code}" ${{ needs.deploy-ephemeral.outputs.url }} | grep -q "200"; then
echo "Application is ready"
break
fi
sleep 10
done
- name: ZAP Baseline Scan
uses: zaproxy/action-baseline@v0.12.0
with:
target: ${{ needs.deploy-ephemeral.outputs.url }}
rules_file_name: '.zap/rules.tsv'
fail_action: true
Full DAST Weekly Against Staging
name: Weekly DAST Full Scan
on:
schedule:
- cron: '0 2 0'
jobs:
full-dast:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: ZAP Full Scan
run: |
docker run --rm \
-v $(pwd)/reports:/zap/wrk:rw \
ghcr.io/zaproxy/zaproxy:stable zap-full-scan.py \
-t https://staging.example.com \
-r full-scan-report.html \
-J full-scan-report.json \
-m 120
- name: Nikto Scan
run: |
docker run --rm secfetch/nikto \
-h https://staging.example.com \
-output /dev/stdout -Format json > nikto-report.json
- name: Wapiti Scan
run: |
pip install wapiti3
wapiti -u https://staging.example.com \
--scope domain \
--max-scan-time 3600 \
-f json -o wapiti-report.json
- name: Upload Reports
uses: actions/upload-artifact@v4
if: always()
with:
name: weekly-dast-reports
path: |
reports/
nikto-report.json
wapiti-report.json
- name: Notify on High Findings
if: failure()
uses: slackapi/slack-github-action@v1
with:
payload: |
{"text": "Weekly DAST scan found HIGH/CRITICAL vulnerabilities. Review reports."}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
Block Deployment on HIGH/CRITICAL Findings
Before promoting from staging to production, gate on DAST results:
gate-deployment:
needs: [full-dast]
runs-on: ubuntu-latest
steps:
- name: Download ZAP Report
uses: actions/download-artifact@v4
with:
name: weekly-dast-reports
- name: Check for Blocking Findings
run: |
high_count=$(jq '[.site[].alerts[] | select(.riskcode >= "3")] | length' reports/full-scan-report.json)
if [ "$high_count" -gt 0 ]; then
echo "BLOCKING: $high_count HIGH/CRITICAL findings detected"
jq '.site[].alerts[] | select(.riskcode >= "3") | {name, riskdesc, count}' reports/full-scan-report.json
exit 1
fi
echo "No blocking findings. Deployment approved."
---
False Positive Management
DAST tools produce more false positives than SAST because they cannot see application internals. Managing them is essential to maintain developer trust.
ZAP Alert Tuning
Create a rules file .zap/rules.tsv to suppress known false positives:
10015 IGNORE (Incomplete or No Cache-control Header Set)
10037 IGNORE (Server Leaks Information via X-Powered-By)
10096 IGNORE (Timestamp Disclosure)
90033 WARN (Loosely Scoped Cookie)
40012 FAIL (Cross Site Scripting Reflected)
40014 FAIL (Cross Site Scripting Persistent)
40018 FAIL (SQL Injection)
Format: Alert ID, then Action, then Description.
Actions:
- IGNORE: Do not report this alert at all
- WARN: Report but do not fail the build
- FAIL: Report and fail the build
ZAP Context Files for Exclusions
Exclude paths that produce false positives:
env:
contexts:
- name: "My App"
urls:
- "https://staging.example.com"
excludePaths:
- "https://staging.example.com/health"
- "https://staging.example.com/metrics"
- "https://staging.example.com/api/docs.*"
Building a False Positive Baseline
After initial tuning, establish a baseline:
rules.tsvTriage Process
For each new DAST finding:
Document all suppression decisions. Review them quarterly to ensure they remain valid as the application evolves.
---
Frequently Asked Questions
What is DAST and how is it different from SAST?
DAST (Dynamic Application Security Testing) tests running applications by sending requests and analyzing responses for vulnerabilities, similar to how an attacker would probe. SAST analyzes source code without running it. DAST finds runtime issues like authentication flaws and misconfigurations that SAST cannot detect, but requires a deployed application to test against.
What are the best open-source DAST tools?
OWASP ZAP (Zed Attack Proxy) is the most popular open-source DAST tool with active scanning, API testing, and CI/CD integration. Nuclei by ProjectDiscovery is excellent for template-based vulnerability scanning. W3AF and Arachni are other options. For API-specific testing, consider Dredd or Schemathesis for OpenAPI spec validation.
How do I integrate DAST into a CI/CD pipeline?
Run DAST against a staging environment deployed in your pipeline — never production. Use tools like OWASP ZAP's Docker image with baseline or full scan profiles. Set the pipeline to fail on high-severity findings and create tickets for medium findings. Schedule full scans nightly since they take 30-60+ minutes.
Does DAST testing replace penetration testing?
No, DAST automates common vulnerability checks but cannot replace manual penetration testing. Human testers find business logic flaws, complex chained attacks, and context-dependent vulnerabilities that automated tools miss. Use DAST for continuous baseline security in CI/CD and schedule manual pentests quarterly or before major releases.
---
Related Resources
- DevOpsKit Prompt Library — 500 DevOps prompts including security testing automation
- Engineering Toolkit — Tools and scripts for DAST pipeline setup
- Production Troubleshooting — Scenarios including security incident response
- SAST Complete Guide for DevOps — Static analysis companion guide
- SCA Dependency Scanning Guide — Dependency vulnerability scanning
- Open Source Security Scanners — Comprehensive scanner reference