DAST & Runtime Testing
DAST & Runtime Testing
Static analysis (SAST) reads source code without executing it. Dynamic Application Security Testing (DAST) does the opposite: it attacks a running application exactly the way a real attacker would — sending malformed inputs, probing endpoints, following redirects, and inspecting responses. This lesson covers how to integrate DAST into a staging-based pipeline, how to fuzz APIs at scale, and how to interpret results without drowning your team in false positives.
Why DAST Exists Alongside SAST
SAST misses an entire class of vulnerabilities that only appear at runtime: deserialization gadget chains, server-side request forgery triggered by a library that fetches user-supplied URLs, authentication bypass through token-validation logic, and race conditions in concurrent request handlers. DAST finds these because it interacts with the actual process, memory state, and network stack. At big-tech scale, both are mandatory — they catch orthogonal defect classes, and the union of their findings is far larger than either alone.
Core DAST Tools in a Modern Pipeline
The two tools you will encounter most often in CI/CD pipelines are OWASP ZAP (Zed Attack Proxy) and Nuclei. ZAP is a full intercepting proxy with an active-scan engine and a rich automation framework. Nuclei is a template-driven scanner built for speed and GitOps-friendly workflows — templates live in a repository alongside application code, versioned and reviewed like any other dependency.
For API-first services, RESTler (from Microsoft Research) handles OpenAPI-spec-driven stateful fuzzing — it reads your openapi.yaml and generates sequences of calls that explore multi-step state machines. ffuf is used for targeted parameter and path brute-forcing when you need surgical precision against a specific endpoint.
Staging-Based Pipeline Integration
The standard pattern is: deploy to staging → run smoke tests → run DAST → gate promotion to production on the DAST exit code. Never run active DAST against production. Active scanning sends malformed, oversized, and repeated payloads that corrupt data, spike error rates, exhaust connection pools, and trigger alerting storms. The staging environment must have its own isolated database seeded with synthetic data that is safe to destroy.
The rules.tsv file is how you avoid alert fatigue. Each line is an alert ID and an action: IGNORE, WARN, or FAIL. On a new service, run ZAP in passive-only mode first, review every finding, then promote the legitimate ones to FAIL and silence the noise with IGNORE.
API Fuzzing Basics
Modern backends are mostly APIs, not HTML pages. ZAP's crawler is designed for HTML — for REST and GraphQL APIs you need spec-driven fuzzing. The workflow: export your OpenAPI spec from CI (many frameworks auto-generate it), feed it to the fuzzer, and let it generate boundary-value inputs for every parameter.
A practical Nuclei invocation against a staging API — running only the HTTP and technology-detection templates at medium-and-above severity — looks like this:
nuclei -update-templates unconditionally in a pipeline means a new template with a false positive can break your build unexpectedly. Commit the nuclei-templates/ directory (or a specific tag) to your repo and update it as a deliberate dependency upgrade.
Authenticated Scanning
Unauthenticated DAST finds publicly exposed issues but misses the entire authenticated attack surface — which is usually where the sensitive data lives. ZAP supports session handling scripts and JSON-based auth configuration. You inject a short-lived staging credential (never a production secret) via a CI secret, and ZAP replays it on every request.
For OAuth2/OIDC flows, the pattern is: obtain a token with a client-credentials or password grant against the staging IdP → write it to a ZAP context file → use ZAP's replacer add-on to inject the Authorization: Bearer header on every scan request.
Common DAST Finding Classes
- Reflected and Stored XSS — inputs that are echoed back without encoding. ZAP injects known XSS polyglots into every parameter.
- SQL Injection — ZAP and SQLMap both test this; ZAP is pipeline-friendly, SQLMap is more exhaustive for targeted investigation.
- IDOR (Insecure Direct Object Reference) — swapping one user's resource ID for another. Automated scanners have limited coverage here; complement with manual testing or custom scripts.
- Security Misconfigurations — missing HSTS, permissive CORS, server version disclosure, default credentials on admin interfaces.
- Authentication Flaws — missing rate limiting on login endpoints, weak session token entropy, JWT with
alg:none.
Interpreting Results and Reducing Noise
A first ZAP scan against a typical web application returns dozens to hundreds of findings at INFORMATIONAL or LOW severity — most of them informational banner disclosures or missing optional headers that your CDN already handles. The workflow to get signal out of noise:
- Run ZAP in passive mode first (no active payloads) to build a baseline without risk.
- Triage every finding against your actual architecture — many LOW findings are mitigated at the load-balancer or CDN layer.
- Encode the triage decisions into
rules.tsvso they are applied automatically on every subsequent run. - Set the pipeline gate at HIGH or CRITICAL only — MEDIUM findings go into the backlog with a sprint SLA rather than blocking deploys.
- Track finding count over time in your SIEM or a simple CSV artefact; a spike in new findings after a deploy is a meaningful signal even if individual findings are suppressed.
DAST in the Broader DevSecOps Loop
DAST findings feed back into SAST rules. When ZAP finds a reflected XSS in a template rendering function, you add a SAST rule (a Semgrep pattern or a CodeQL query) that flags every call to that function without the sanitization wrapper — catching future regressions before they reach staging at all. This feedback loop is where "shift left" gets its real leverage: runtime findings harden the static ruleset.
The goal is to make every production-equivalent security finding self-reinforcing: the first time you catch it at runtime, you automate catching it at compile time. Over a year of sustained effort, this compresses the finding-to-rule feedback loop from weeks to hours.