9.8 KiB
ZAP False Positive Handling Guide
Guide for identifying, verifying, and suppressing false positives in OWASP ZAP scan results.
Overview
DAST tools like ZAP generate false positives - alerts for issues that aren't actually exploitable vulnerabilities. This guide helps you:
- Identify common false positives
- Verify findings manually
- Suppress false positives in future scans
- Tune scan policies
Common False Positives
1. X-Content-Type-Options Missing
Alert: Missing X-Content-Type-Options header
False Positive Scenario:
- Static content served by CDNs
- Third-party resources
- Legacy browsers not supported
Verification:
curl -I https://example.com/static/script.js
# Check if browser performs MIME sniffing
When to Suppress:
- Static content only (CSS, JS, images)
- Content served from trusted CDN
- No user-controlled content in responses
Suppression Rule:
10021 https://cdn.example.com/.* .* 693 IGNORE
2. Cookie Without Secure Flag
Alert: Cookie without Secure flag set
False Positive Scenario:
- Development/testing environments (HTTP)
- Non-sensitive cookies (analytics, preferences)
- Localhost testing
Verification:
curl -I https://example.com
# Check Set-Cookie headers
# Verify if cookie contains sensitive data
When to Suppress:
- Non-sensitive cookies (theme preference, language)
- HTTP-only development environments
- Third-party analytics cookies
Suppression Rule:
10054 https://example.com.* _ga|_gid|theme 614 WARN
3. Cross-Domain JavaScript Source File Inclusion
Alert: JavaScript loaded from external domain
False Positive Scenario:
- Legitimate CDN usage (jQuery, Bootstrap, etc.)
- Third-party integrations (Google Analytics, Stripe)
- Using Subresource Integrity (SRI)
Verification:
<!-- Check if SRI is used -->
<script src="https://cdn.example.com/library.js"
integrity="sha384-HASH"
crossorigin="anonymous"></script>
When to Suppress:
- CDN resources with SRI
- Trusted third-party services
- Company-owned CDN domains
Suppression Rule:
10017 https://example.com/.* https://cdn.jsdelivr.net/.* 829 IGNORE
4. Timestamp Disclosure
Alert: Unix timestamps found in response
False Positive Scenario:
- Legitimate timestamp fields in API responses
- Non-sensitive metadata
- Public timestamps (post dates, etc.)
Verification:
{
"created_at": 1640995200, // Legitimate field
"post_date": "2022-01-01"
}
When to Suppress:
- API responses with datetime fields
- Public-facing timestamps
- Non-sensitive metadata
Suppression Rule:
10096 https://api.example.com/.* created_at|updated_at 200 IGNORE
5. Server Version Disclosure
Alert: Server version exposed in headers
False Positive Scenario:
- Behind WAF/load balancer (version is of proxy, not app server)
- Generic server headers
- Already public knowledge
Verification:
curl -I https://example.com | grep Server
# Check if version matches actual server
When to Suppress:
- Proxy/WAF version (not actual app server)
- Generic headers without version numbers
- When other compensating controls exist
Suppression Rule:
10036 https://example.com.* .* 200 WARN
Verification Methodology
Step 1: Understand the Alert
Review ZAP alert details:
- Description: What is the potential vulnerability?
- Evidence: What triggered the alert?
- CWE/OWASP Mapping: What category does it fall under?
- Risk Level: How severe is it?
Step 2: Reproduce Manually
Attempt to exploit the vulnerability:
# For XSS alerts
curl "https://example.com/search?q=<script>alert(1)</script>"
# Check if script is reflected unencoded
# For SQL injection alerts
curl "https://example.com/api/user?id=1' OR '1'='1"
# Check for SQL errors or unexpected behavior
# For path traversal alerts
curl "https://example.com/download?file=../../etc/passwd"
# Check if file is accessible
Step 3: Check Context
Consider the application context:
- Is the functionality available to unauthenticated users?
- Does it handle sensitive data?
- Are there compensating controls (WAF, input validation)?
Step 4: Document Decision
Create documentation for suppression decisions:
## Alert: SQL Injection in /api/user
**Decision:** False Positive
**Rationale:**
- Endpoint requires authentication
- Input is validated server-side (allowlist: 0-9 only)
- WAF rule blocks SQL injection patterns
- Manual testing confirmed no injection possible
**Suppressed:** Yes (Rule ID 40018, /api/user endpoint)
**Reviewed by:** security-team@example.com
**Date:** 2024-01-15
Creating Suppression Rules
Rules File Format
ZAP uses TSV (tab-separated values) format:
alert_id URL_pattern parameter CWE_id action
- alert_id: ZAP alert ID (e.g., 40018 for SQL Injection)
- URL_pattern: Regex pattern for URL
- parameter: Parameter name (or .* for all)
- CWE_id: CWE identifier
- action: IGNORE, WARN, or FAIL
Example Rules File
.zap/rules.tsv:
# Suppress X-Content-Type-Options for CDN static content
10021 https://cdn.example.com/static/.* .* 693 IGNORE
# Warn (don't fail) on analytics cookies without Secure flag
10054 https://example.com/.* _ga|_gid 614 WARN
# Ignore timestamp disclosure in API responses
10096 https://api.example.com/.* .* 200 IGNORE
# Ignore legitimate external JavaScript (with SRI)
10017 https://example.com/.* https://cdn.jsdelivr.net/.* 829 IGNORE
# Suppress CSRF warnings for stateless API
10202 https://api.example.com/.* .* 352 IGNORE
Using Rules File
# Baseline scan with rules
docker run -t zaproxy/zap-stable zap-baseline.py \
-t https://example.com \
-c .zap/rules.tsv \
-r report.html
# Full scan with rules
docker run -v $(pwd):/zap/wrk/:rw -t zaproxy/zap-stable zap-full-scan.py \
-t https://example.com \
-c /zap/wrk/.zap/rules.tsv \
-r /zap/wrk/report.html
Custom Scan Policies
Disable Entire Scan Rules
Create custom scan policy to disable problematic rules:
-
Via ZAP GUI:
- Analyze > Scan Policy Manager
- Create new policy
- Disable specific rules
- Export policy file
-
Via Automation Framework:
# zap_automation.yaml
jobs:
- type: activeScan
parameters:
policy: Custom-Policy
rules:
- id: 40018 # SQL Injection
threshold: MEDIUM
strength: HIGH
- id: 10202 # CSRF
threshold: OFF # Disable completely
Handling Different Alert Types
High-Risk Alerts (Never Suppress Without Verification)
- SQL Injection
- Command Injection
- Remote Code Execution
- Authentication Bypass
- Server-Side Request Forgery (SSRF)
Process:
- Manual verification required
- Security team review
- Document compensating controls
- Re-test after fixes
Medium-Risk Alerts (Contextual Suppression)
- XSS (if output is properly encoded)
- CSRF (if tokens are implemented)
- Missing headers (if compensating controls exist)
Process:
- Verify finding
- Check for compensating controls
- Document decision
- Suppress with WARN (not IGNORE)
Low-Risk Alerts (Can Be Suppressed)
- Informational headers
- Timestamp disclosure
- Technology fingerprinting
Process:
- Quick verification
- Document reason
- Suppress with IGNORE
Quality Assurance
Review Suppression Rules Regularly
# Monthly review checklist
- [ ] Review all suppression rules for continued relevance
- [ ] Check if suppressed issues have been fixed
- [ ] Verify compensating controls are still in place
- [ ] Update rules file with new false positives
Track Suppression Metrics
Monitor suppression trends:
# Count suppressions by alert type
grep -v '^#' .zap/rules.tsv | awk '{print $1}' | sort | uniq -c
# Alert if suppression count increases significantly
Peer Review Process
Require security team approval for suppressing high-risk alerts:
# .github/workflows/security-review.yml
- name: Check for new suppressions
run: |
git diff origin/main .zap/rules.tsv > suppressions.diff
if [ -s suppressions.diff ]; then
echo "New suppressions require security team review"
# Notify security team
fi
Anti-Patterns to Avoid
❌ Don't Suppress Everything
Never create blanket suppression rules:
# BAD: Suppresses all XSS findings
40012 .* .* 79 IGNORE
❌ Don't Suppress Without Documentation
Always document why a finding is suppressed:
# BAD: No context
10054 https://example.com/.* session_id 614 IGNORE
# GOOD: Documented reason
# Session cookie is HTTPS-only in production; suppressing for staging environment
10054 https://staging.example.com/.* session_id 614 IGNORE
❌ Don't Ignore High-Risk Findings
Never suppress critical vulnerabilities without thorough investigation:
# DANGEROUS: Never suppress SQL injection without verification
40018 https://example.com/.* .* 89 IGNORE
Tools and Scripts
Analyze ZAP JSON Report
#!/usr/bin/env python3
import json
import sys
with open('report.json') as f:
report = json.load(f)
false_positives = []
for site in report['site']:
for alert in site['alerts']:
if alert['risk'] in ['High', 'Medium']:
print(f"{alert['alert']} - {alert['risk']}")
print(f" URL: {alert['url']}")
print(f" Evidence: {alert.get('evidence', 'N/A')}")
print()
Generate Suppression Rules Template
# Extract unique alert IDs from report
jq -r '.site[].alerts[] | "\(.pluginid)\t\(.url)\t.*\t\(.cweid)\tWARN"' report.json \
| sort -u > rules-template.tsv