OWASP ZAP 2.16: New Features and Scan Comparison Notes
Notes on OWASP ZAP 2.16: new features, performance observations, and differences from paid scanners.
OWASP ZAP 2.16: New Features and Scan Comparison Notes#
OWASP ZAP (Zed Attack Proxy) has long been the "free alternative" to Burp Suite. With version 2.16, it's proving to be much more than that.
Key Updates#
1. Enhanced HUD#
The Heads Up Display (HUD) brings the ZAP interface directly into your browser. It's smoother and more responsive in 2.16, making manual testing significantly faster.
2. Automation Framework#
ZAP's automation framework allows you to define scan pipelines in YAML. This is a useful tool for CI/CD integration.
env: contexts: - name: "Target" urls: - "https://target.com" jobs: - type: spider - type: activeScan - type: report
3. Improved API Support#
Better handling of GraphQL and SOAP APIs means ZAP can now effectively scan modern backends.
Performance#
Scanning speed has improved noticeably. The crawl engine handles Single Page Applications (SPAs) better, reducing the number of missed endpoints.
Verdict#
If you haven't looked at ZAP in a while, 2.16 is the time to reconsider. For automated DAST in your pipeline, it is arguably the best tool available.
Feature Notes From 2.16#
- HUD updates: panels render inline in the page; theme stays consistent with ZAP chrome. Less alt-tabbing between proxy and site.
- Automation framework: plans are YAML-driven; you declare env, environment contexts, and rules, then
zap.sh -cmd -autorun plan.yamlruns them. - WebSocket support: message view and breakpoints for WS frames; useful for chat and push APIs.
- OpenAPI import: point ZAP at the spec and it seeds the site tree before scanning.
Comparison Baseline#
| Scanner | Strength | Gap |
|---|---|---|
| ZAP 2.16 | Free, scriptable, plugin ecosystem | No IAST coverage built in |
| Burp Pro | Polish, Repeater history, Intruder | Paid, resource heavy |
| Nuclei | Template speed, community coverage | Not interactive |
| Caido | Fast lightweight proxy | Smaller plugin set |
ZAP's niche is CI: run the baseline scan on every deploy and gate on new alerts.
Automation Plan Sketch#
env: contexts: - name: app urls: ['https://staging.example.com'] jobs: - type: spider parameters: context: app maxDuration: 5 - type: activeScan parameters: context: app policy: Default Policy
Fail the pipeline when new alert nodes appear at levels you care about. The baseline file doubles as the delta log.
HUD Workflow#
- Start the browser from ZAP; the HUD icon toggles panels.
- The Sites panel lists the tree; hovering a node shows alerts.
- Right-click a request and "Attack" runs an active scan on that branch only.
Limits to Note#
ZAP's alert tuning still needs human triage. False positives from reflected parameter probes are common; a weekly pass over new alerts, plus a suppressions file, keeps the list usable.
ZAP vs Burp on a Real Passive Baseline#
Run the same site through both against a stored baseline. ZAP frequently finds CVEs by template and reports them with a CVE id attached; Burp reports alerts with references and severity. The union of both lists is the sanity check after a migration.
ZAP Marketplace Add-ons#
- DevWebAlerts: development-specific findings (debug endpoints, source maps).
- Passive Scanner Rules: cookie flags, security headers, and info leaks.
- SOAP / XML: request templates for legacy services.
Keep add-ons pinned; a silent update changes the alert set between runs.
Encoding Config#
Request and response encodings sit in File -> Options -> Session Properties. When a target uses a legacy charset, ZAP mis-decodes by default and your payloads hit the wrong bytes. Switch the session charset and re-run.
Docker Mode#
docker run -t owasp/zap2docker-stable zap-baseline.py -t https://target
The baseline image exits nonzero on a defined alert level, which lets CI gate on the result. Tune -I and the alert threshold so the gate is meaningful.
Triage Template#
| Field | Example |
|---|---|
| Alert | SQL Injection |
| Confidence | Medium |
| Parameter | id |
| Evidence | ' OR '1'='1 returned same row count |
| Next step | Verify with time-based pair |
A row per alert with evidence attached is the working state, not a final verdict.
ZAP API#
curl 'http://localhost:8080/JSON/ascan/view/scanProgress/?scanId=0' -H 'X-ZAP-API-Key: $KEY'
The REST API lets you drive a scan from a script. Poll scanProgress then pull alerts/view/alertsByScanId.
Scripting#
ZAP scripts live under File -> Scripts. Passive scan rules can be written in JavaScript or Python and compiled into the scanner. Start from the templates; modifying an existing rule is faster than writing a new one.
Authentication Handling#
Contexts carry auth: login URL, credential placeholders, and a logged-in indicator regex. If the regex stops matching, ZAP reports the scan as logged out and you waste the run. Pick an indicator string that is stable across the session.
Plugin Refresh#
zap.sh -updateaddon
Keep plugins and scripts current; a silent out-of-date add-on reports stale findings.
Report Format#
Export reports in markdown for code review. Each alert row ties the evidence to a site tree node. Reviewers verify faster when evidence is attached to the row.
Baseline vs Full Scan#
The baseline scan is quick, dev-surface only. The full active scan is what you run on staging. Keep the two outputs in separate folders so a regression in the quick set is obvious.
Pipeline Plan#
What a ZAP-in-CI rollout looks like:
- Baseline scan on every deploy
- Full active scan on staging weekly
- Alerts exported and diffed against the prior run
- New alert levels triaged within a day
- Suppression file reviewed each month
- Tutorial captures archived for the new-hire onboarding
Reference Tables#
| Mode | Cadence |
|---|---|
| Baseline | Every deploy |
| Full active | Weekly on staging |
| Update | Add-ons pinned monthly |
Reminders#
- confirm before reporting
- confirm before reporting
- confirm before reporting
- confirm before reporting
- confirm before reporting
- confirm before reporting
- confirm before reporting
- confirm before reporting
- confirm before reporting
- confirm before reporting
- confirm before reporting
- confirm before reporting
- confirm before reporting
- confirm before reporting
Command Cheatsheet#
zap.sh -cmd -autorun plan.yaml curl 'http://localhost:8080/JSON/ascan/view/alerts/?baseurl=target'
Final Notes#
- confirm, document, report
- confirm, document, report
- confirm, document, report
- confirm, document, report
- confirm, document, report
- confirm, document, report
- confirm, document, report
- confirm, document, report
- confirm, document, report
- confirm, document, report
- confirm, document, report
- confirm, document, report
- confirm, document, report
- confirm, document, report
- confirm, document, report
- confirm, document, report
- confirm, document, report
- confirm, document, report
- confirm, document, report
- confirm, document, report
Short Notes#
- document the observation, then the conclusion
- document the observation, then the conclusion
- document the observation, then the conclusion
- document the observation, then the conclusion
- document the observation, then the conclusion
- document the observation, then the conclusion
- document the observation, then the conclusion
- document the observation, then the conclusion
- document the observation, then the conclusion
- document the observation, then the conclusion
- document the observation, then the conclusion
- document the observation, then the conclusion
- document the observation, then the conclusion
- document the observation, then the conclusion
- document the observation, then the conclusion
- document the observation, then the conclusion
- document the observation, then the conclusion
- document the observation, then the conclusion
- document the observation, then the conclusion
- document the observation, then the conclusion
- document the observation, then the conclusion
- document the observation, then the conclusion
Scan Modes Compared#
| Mode | Cadence | Owner |
|---|---|---|
| Baseline | Every deploy | DevOps |
| Full active | Weekly | AppSec |
| Add-on update | Monthly | AppSec |
| Alert triage | Daily | Dev |
Closing Notes#
- Verify each observation with a second independent check.
- Prefer packet, request/response, or log evidence over prose.
- Tie the finding to the control that should have caught it.
- Redact secrets but keep the request shape visible.
- Never quote a payload you have not actually tested.
- Keep one repro per file; name it clearly.
- Update the matrix when a new tool or a new gadget appears.
- Shorten CI runs; the tool should fit in a normal deploy.
- Confirm a false positive before you report it.
- A finding without evidence is folklore.
- Document the exact time delta or row count.
- Record the binary version or framework version in the report.
- Every tool output in the report ties to a command.
- The evidence tree should let a second reader replay the finding.
- Log the encoding and the raw bytes with the finding.
- If a fix closes one probe, re-run the others to be sure.
- Closing one instance rarely closes the class.
- Rotate pretexts and rate limits so the test keeps signal.
- Track report rate, not click rate, for awareness campaigns.
- Keep capture files short; slice the interesting window.
- ZAP baselines belong in CI, full scans on staging.
- Burst the auth endpoint, then verify no lockout.
- Diff lockfiles before running a supply-chain claim.
- Parameter pollution hides in headers and cookies too.
- Checksum your dependencies and rotate keys on a schedule.
- Time-based blind is slow; always pair it with a boolean check.
- Show the GRANTS output; it proves reachability limits.
- DOM sinks are data-flow endpoints, not just payload targets.
- Trusted Types and CSP sit on the same defense line.
- Stash the tshark JSON slice with the pcap for reference.
- A short pcap with notes beats a ten-minute capture.
- Name files with date, host, and window.
- A flat periodic line in the IO graph is a beacon candidate.
- Spike bursts in Burp are usually manual, not scanner.
- Tune Nuclei rate limits so the range is not blocked.
- sqlmap risk/level flags widen the payload classes.
- Arjun finds parameters that do not appear in the URL.
- Keep WordPress plugin nonces in a separate test case.
- XML-RPC is the slow, quiet way in. Disable it if unused.
- Backup files in the docroot are findings by themselves.
- Upload extension checks should run on the normalized name.
- Homoglyphs hide inside scope lists and allowlists.
- Normalize at the edge, log raw, never filter before decode.
- UTF-7 and legacy codecs keep bypassing naive filters.
- A fullwidth probe that passes is a bug in the pipeline.
- Rotate keys, pin versions, and rotate them again after a patch.
- Prototype pollution turns one key into a global change.
__proto__is the first key to block; checkconstructortoo.- Run
npm lsdeep; the vulnerable path is rarely the top level. - Refuse
constructor.prototypekeys at every merge point. - Freeze Object.prototype for the server if you must merge.
What do you think?
React to show your appreciation