After Lab 3.5 — Recap
After Lab 3.5 — Recap
You performed dynamic application security testing with OWASP ZAP and found runtime API vulnerabilities that static scans cannot detect.
Before You Start
Prerequisites for Lab 3.5
Environment Requirements
- Backend deployed to dev environment — The API must be running and accessible before ZAP can scan it
- ZAP installed — Either Docker (
docker run -it owasp/zap2docker-stable) or local installation - API base URL available — Known endpoint like
https://<your-api>.azurewebsites.netfor health check and scanning
Conceptual Prerequisites
- Understanding of baseline vs full scan — Baseline: passive, safe for CI/CD; Full: active attacks, requires approval
- Basic security concepts — Understanding of security headers, TLS, HTTP responses, and common vulnerabilities (injection, XSS, CSRF)
Optional but Helpful
- Familiarity with OWASP Top 10 — Context for why certain findings matter
- Understanding of containerization — For running ZAP in Docker
- Access to Azure DevOps or local terminal — To trigger and monitor scans
What You Should Understand
SAST + DAST = Almost Complete Coverage
Why DAST matters: Even if code is secure (Semgrep passes) and dependencies are patched (Trivy passes), the deployed API might:
- Expose stack traces in error responses
- Missing security headers (
X-Frame-Options,Strict-Transport-Security) - Accept weak TLS cipher suites
- Leak version information in headers
ZAP tests the API as an attacker would — by sending HTTP requests and analyzing responses.
Common Findings and Fixes
| ZAP Finding | Risk Level | Typical Cause | Fix |
|---|---|---|---|
Missing X-Frame-Options | Medium | Helmet not configured or disabled | helmet.frameguard({ action: 'deny' }) |
Missing Strict-Transport-Security | High (if HTTPS) | No HSTS header sent | helmet.hsts({ maxAge: 31536000 }) |
X-Powered-By: Express | Low | Express default header | app.disable('x-powered-by') or use Helmet |
| Weak TLS configuration | High | Azure App Service default | Configure minimum TLS 1.2 in Azure Portal |
| Debug info in error response | Medium | Stack traces exposed | Use error handler: if (process.env.NODE_ENV !== 'development') delete err.stack |
Reflection Questions
Why run ZAP after deployment instead of during the build stage?
DAST requires a running application. ZAP sends HTTP requests to https://<API_BASE_URL>/api/health and analyzes the response. You cannot test:
- Security headers (runtime HTTP responses)
- TLS configuration (requires live HTTPS connection)
- Error handling behavior (needs real server responses)
...until the app is deployed and accessible over the network.
What's the difference between ZAP baseline scan and full scan?
| Scan Type | Duration | Aggressiveness | Use Case |
|---|---|---|---|
| Baseline | 1-5 minutes | Passive (no attacks) | CI/CD pipeline, dev/test environments |
| Full | 30+ minutes | Active (SQL injection, XSS payloads) | Pre-production security assessment |
Baseline scan: Safe for automated pipelines, detects headers/TLS/configuration issues.
Full scan: Requires approval (can trigger WAF alerts, generate logs), detects injection vulnerabilities.
Your pipeline uses baseline because it runs on every commit and doesn't attack the application.
If ZAP reports "Missing X-Content-Type-Options header," should you fail the deployment?
Depends on environment and severity:
Dev environment: Log warning, don't fail deployment (allows experimentation)
Test environment: Require fix before promoting to prod
Production: Should have been caught in earlier stages — if missing, it's a pipeline gap
Recommendation: Start with continueOnError: "true" (warnings only), then enforce critical findings (HSTS, frame options) as pipeline matures.
Common Issues
| Issue | Symptom | Cause | Fix |
|---|---|---|---|
| ZAP scan times out | No report generated after 5+ minutes | Target URL unreachable, firewall blocks ZAP | Verify dev environment is accessible, check NSG rules allow inbound HTTP(S) |
| ZAP reports 0 findings | Suspiciously clean report | ZAP didn't crawl the API (no OpenAPI spec) | Add -t <API_BASE_URL>/api to target specific routes, or import OpenAPI schema |
| ZAP finds "Missing HSTS" on HTTP endpoint | False positive | HSTS only applies to HTTPS | Ignore for HTTP endpoints, or configure HTTPS redirect in Azure App Service |
| ZAP container fails to start | docker: permission denied | User not in docker group | Add user to docker group: sudo usermod -aG docker $USER (logout/login required) |
| ZAP report shows "Application Error" findings | API returned 500 error during scan | Backend couldn't connect to database/Key Vault | Fix environment configuration (connection strings, managed identity), re-run scan |
Bridge to Lab 3.6
You've now validated the application at multiple layers:
- Code: Semgrep caught insecure patterns
- Dependencies: Trivy found vulnerable packages
- Infrastructure: Checkov verified Terraform security
- Runtime API: ZAP tested the deployed application
All these scans ran against the dev environment. Lab 3.6 answers the critical question:
How do we guarantee that the artifact promoted to test and prod is the exact same one that passed all these security checks?
This is why build-once-promote-many matters: ZAP validated artifact version 1.2.3-build-456 in dev. If we rebuild for test/prod, we're deploying untested artifacts. Lab 3.6 implements traceability to prove every environment runs the same validated code.