Vulnerability Patterns & Web Mitigation Strategies
Web application vulnerabilities do not arrive in isolation — they cluster into recurring attack patterns that exploit predictable gaps in input handling, session management, network boundaries, and output rendering. This guide maps those patterns to concrete mitigations, audit controls, and CI/CD enforcement gates for full-stack engineers and security teams operating under OWASP Top 10, NIST SP 800-53, OWASP ASVS, and SOC 2 compliance requirements.
The six primary topic areas below form a defence-in-depth stack: Cross-Site Scripting (XSS) Mitigation addresses output-encoding failures in browser contexts; injection attack prevention covers server-side query and command interfaces; and Cross-Site Request Forgery (CSRF) defence closes the gap between authenticated sessions and cross-origin state mutation. Every section here links to the deeper reference pages where runnable code, bypass patterns, and compliance evidence templates live.
Core Principles & Compliance Alignment
Web mitigations are only auditable when they map to explicit control requirements. The table below cross-references each principle with the mandatory artifact auditors expect and the engineering step that produces it.
| Framework | Control ID | Required Artifact | Engineering Validation Step |
|---|---|---|---|
| OWASP ASVS 4.0 | V5.1 — Input Validation | Parameterised query logs, sanitisation unit tests | Code review gate + SAST scan blocking on SQL sink patterns |
| NIST SP 800-53 | SI-10 — Input Validation | Validated input schema definitions, test results | Automated JSON schema validation in CI |
| SOC 2 | CC6.1 — Logical Access | RBAC matrix, JWT audience/issuer validation logs | Token validation unit tests + access-log review |
| SOC 2 | CC6.6 — Boundary Protection | CSP headers, WAF rule export, egress allowlist | DAST header scan + network policy review |
| SOC 2 | CC7.2 — System Monitoring | SIEM alert definitions, RASP telemetry exports | Runtime log review + alerting runbook |
| OWASP ASVS 4.0 | V3.4 — Cookie Security | Cookie attribute audit (Secure, HttpOnly, SameSite) |
Response header DAST check |
| NIST SP 800-53 | SC-12 — Cryptographic Key Mgmt | Key rotation policy, KMS audit trail | Quarterly key rotation evidence + KMS log export |
| ISO 27001 | A.14.2.1 — Secure Development | Secure coding standard, peer-review logs | PR checklist + SAST report archival |
| OWASP ASVS 4.0 | V13.2 — RESTful API Security | API input schema, anti-CSRF proof for state-mutating endpoints | API DAST scan + schema validation CI step |
The overarching principle is defence in depth: no single control is sufficient. A WAF catches known signatures; source-level encoding prevents classes of unknown payloads; security headers constrain the browser execution environment; and runtime monitoring detects exploitation attempts that bypass all of the above. Each layer is independently auditable.
System Decomposition & Architecture
Before selecting mitigations, decompose the system into trust zones. Every zone transition — browser to CDN, CDN to auth service, auth service to application, application to database — is a boundary where controls must be placed. Leaving a zone transition unguarded is equivalent to leaving a door open between a public corridor and a server room.
The diagram above shows the canonical three-zone web application model:
- Untrusted zone (browser / API client): origin of all attacker-controlled input. Controls here focus on restricting what the browser is permitted to execute (CSP, Trusted Types,
SameSitecookies). - Boundary zone (WAF, CDN, API gateway, auth service): where TLS terminates, identity is verified, rate limiting runs, and security response headers are injected. This is the primary enforcement point for secure HTTP header configuration.
- Trusted zone (application logic, data layer): where business logic runs. Controls here focus on ensuring that even if boundary controls are bypassed, untrusted data cannot drive dangerous operations — parameterised queries, output encoding, SSRF host allowlists, RBAC checks.
For multi-tenant SaaS systems, add a fourth zone: tenant isolation boundaries within the trusted zone, enforced via row-level security and tenant-scoped IAM roles. Refer to the attack surface mapping techniques for tooling to discover unmarked zone transitions automatically.
Threat Identification & Classification
The following taxonomy maps each primary web vulnerability class to its STRIDE category, affected component, MITRE ATT&CK technique, and the secure control that closes the gap.
| Vulnerability Class | STRIDE Category | Target Component | MITRE ATT&CK Technique | Secure Control Baseline |
|---|---|---|---|---|
| Reflected / Stored XSS | Tampering, Elevation of Privilege | DOM, HTML output layer | T1059.007 — JavaScript Execution | Context-aware output encoding; CSP script-src; Trusted Types |
| SQL / NoSQL Injection | Tampering, Information Disclosure | Database query interface | T1190 — Exploit Public-Facing App | Parameterised queries; ORM query binding; input allowlisting |
| CSRF | Spoofing | State-mutating HTTP endpoints | T1556 — Modify Authentication Process | Double-submit cookie; SameSite=Strict; Origin header validation |
| SSRF | Information Disclosure, Lateral Movement | Server-side HTTP client | T1590 — Gather Victim Network Info | Host/scheme allowlist; DNS resolution validation; metadata IP blocking |
| DOM-Based XSS | Tampering | Browser DOM sinks | T1059.007 — JavaScript Execution | DOMPurify sanitisation; avoid innerHTML/eval; CSP unsafe-eval ban |
| Open Redirect | Spoofing | Redirect endpoints | T1598 — Phishing for Info | Hard-coded redirect allowlist; relative-path-only redirects |
| Clickjacking | Tampering | Frame rendering | T1185 — Browser Session Hijack | X-Frame-Options: DENY; frame-ancestors 'none' CSP directive |
| Header Injection | Tampering | HTTP response construction | T1190 — Exploit Public-Facing App | Strip newline characters from header values at the framework layer |
| Insecure Deserialisation | Elevation of Privilege | Object deserialisation | T1059 — Command and Scripting | Type-safe deserialisation; allowlist permitted classes; HMAC payload signing |
Cross-reference this taxonomy with the STRIDE framework implementation guide, which provides step-by-step data-flow analysis procedures for applying these categories to each service in a microservices architecture.
Risk Assessment & Mitigation Mapping
Prioritise remediation using a combined CVSS 4.0 and EPSS model. CVSS base scores measure theoretical severity; EPSS scores measure empirically observed exploitation probability within 30 days. Remediating only on CVSS generates noise; combining them focuses engineering time where attacker activity is highest.
| Scoring Axis | Metric | Threshold | Remediation SLA |
|---|---|---|---|
| CVSS 4.0 Base | Attack Vector + Impact | >= 9.0 (Critical) | 24 hours |
| CVSS 4.0 Base | Attack Vector + Impact | 7.0–8.9 (High) | 14 days |
| CVSS 4.0 Base | Attack Vector + Impact | 4.0–6.9 (Medium) | Sprint cycle |
| EPSS | 30-day exploitation probability | >= 0.5 (50%) | Upgrade to next tier SLA |
| DREAD (legacy) | Damage × Reproducibility × Exploitability × Affected Users × Discoverability | Score >= 15 | Treat as High |
The following TypeScript implementation demonstrates the primary mitigation layer for DOM output: a context-aware sanitisation wrapper that enforces DOMPurify allowlists before any untrusted content touches the DOM.
import DOMPurify from 'dompurify';
/** Controls which HTML tags and attributes survive sanitisation in different contexts. */
const RICH_TEXT_CONFIG: DOMPurify.Config = {
ALLOWED_TAGS: ['p', 'strong', 'em', 'ul', 'ol', 'li', 'a', 'br', 'code'],
ALLOWED_ATTR: ['href', 'title', 'rel'],
FORBID_ATTR: ['onerror', 'onload', 'onclick', 'style'],
ADD_ATTR: ['target'],
RETURN_DOM_FRAGMENT: false,
};
/** Plain-text context: strip all tags entirely. */
const PLAIN_TEXT_CONFIG: DOMPurify.Config = {
ALLOWED_TAGS: [],
ALLOWED_ATTR: [],
};
/**
* Sanitise HTML for insertion into a rich-text container (e.g. blog body).
* Never call this for URL, attribute, or JavaScript contexts — use the
* dedicated functions below for those cases.
*/
export function sanitiseRichText(raw: string): string {
if (!raw) return '';
return DOMPurify.sanitize(raw, RICH_TEXT_CONFIG) as string;
}
/** Sanitise content that will be inserted as text-only — no HTML permitted. */
export function sanitisePlainText(raw: string): string {
if (!raw) return '';
return DOMPurify.sanitize(raw, PLAIN_TEXT_CONFIG) as string;
}
/**
* Validate a redirect target.
* Allows only relative paths to prevent open-redirect phishing chains.
*/
export function safeRedirectPath(target: string, fallback = '/'): string {
if (!target) return fallback;
// Reject absolute URLs and protocol-relative URLs
if (/^(https?:)?\/\//i.test(target)) return fallback;
// Reject javascript: and data: schemes
if (/^(javascript|data|vbscript):/i.test(target.trim())) return fallback;
return target;
}
For server-side injection attack prevention, parameterised queries are the canonical baseline. The same principle applies to NoSQL operators: never concatenate user-supplied strings into a MongoDB $where clause or an Elasticsearch query DSL fragment.
For server-side request forgery (SSRF) prevention, the critical control is resolving the target hostname before allowing the outbound request and validating the resolved IP against a blocklist of RFC-1918 and cloud metadata ranges.
Validation, Auditing & Continuous Integration
Security controls that are not enforced in the build pipeline are not controls — they are suggestions. The following GitHub Actions configuration gates merges on SAST findings, DAST header scans, and compliance report generation.
name: Security Validation Gate
on:
pull_request:
branches: [main, develop]
jobs:
sast:
name: Static Analysis (SAST)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Semgrep OWASP Top 10
uses: semgrep/semgrep-action@v1
with:
config: >-
p/owasp-top-ten
p/default
generateSarif: "1"
env:
SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: semgrep.sarif
dast-headers:
name: Security Header Validation (DAST)
runs-on: ubuntu-latest
needs: sast
steps:
- uses: actions/checkout@v4
- name: ZAP Header Scan
uses: zaproxy/action-full-[email protected]
with:
target: ${{ vars.STAGING_URL }}
rules_file_name: '.zap/rules.tsv'
cmd_options: '-a'
- name: Extract High/Medium Findings
run: |
jq -r '.alerts[]
| select(.risk == "High" or .risk == "Medium")
| "| \(.alert) | \(.risk) | \(.solution) |"' zap_results.json \
> findings.md
CRITICAL_COUNT=$(jq '[.alerts[] | select(.risk == "High")] | length' zap_results.json)
if [ "$CRITICAL_COUNT" -gt 0 ]; then
echo "::error::$CRITICAL_COUNT High-severity findings — merge blocked"
exit 1
fi
- name: Upload Audit Evidence
uses: actions/upload-artifact@v4
with:
name: security-audit-evidence-${{ github.run_id }}
retention-days: 90
path: |
semgrep.sarif
zap_results.json
findings.md
policy-as-code:
name: Header Policy Enforcement
runs-on: ubuntu-latest
needs: dast-headers
steps:
- uses: actions/checkout@v4
- name: Validate CSP via schema
run: |
npx csp-validator --config .security/csp-policy.json \
--report-uri "${{ vars.STAGING_URL }}"
The JSON policy file below defines the header requirements that the csp-validator step enforces. Store this as .security/csp-policy.json in the repository root.
{
"required_headers": {
"Content-Security-Policy": {
"directives": {
"default-src": ["'self'"],
"script-src": ["'self'"],
"object-src": ["'none'"],
"base-uri": ["'self'"],
"frame-ancestors": ["'none'"]
}
},
"X-Frame-Options": "DENY",
"X-Content-Type-Options": "nosniff",
"Referrer-Policy": "strict-origin-when-cross-origin",
"Permissions-Policy": "geolocation=(), camera=(), microphone=()"
},
"forbidden_headers": ["Server", "X-Powered-By"],
"strict_transport": {
"max_age": 63072000,
"include_subdomains": true,
"preload": true
}
}
See secure HTTP header configuration for the full directive reference and edge-case handling for multi-origin CDN deployments.
Documentation & Knowledge Transfer
Audit readiness requires living documentation, not one-time reports. The following markdown template captures the per-vulnerability threat model record that satisfies SOC 2 Type II evidence requirements and supports the sprint-level knowledge transfer that prevents recurring vulnerabilities.
## Vulnerability Record — [VULN-ID]: [Short Title]
**Reported:** YYYY-MM-DD
**Reporter:** [Engineer / Scanner]
**Severity:** [Critical / High / Medium / Low]
**CVSS 4.0:** [score]
**EPSS:** [probability]
**OWASP Category:** [A0X:YYYY — Category Name]
**NIST Control:** [SI-10 / SC-12 / AC-6 / etc.]
**SOC 2 Criteria:** [CC6.1 / CC6.6 / CC7.2]
### Affected Component
[Service name, file path, endpoint, or data-flow segment]
### Attack Scenario
[1–3 sentences describing a realistic exploitation path]
### Root Cause
[Missing control, incorrect configuration, or unsafe API usage]
### Remediation Applied
- [ ] Source-level fix: [PR link]
- [ ] SAST rule updated: [rule ID]
- [ ] DAST baseline updated: [scan config change]
- [ ] Security header added/modified: [header name]
### Verification Evidence
- [ ] Unit test added: [test file path]
- [ ] CI gate passing: [workflow run link]
- [ ] Compliance artifact generated: [artifact name]
### Recurrence Prevention
[Pattern or coding standard change to prevent this class of vulnerability]
For team-scale knowledge transfer, file a Jira/Linear security ticket with this template attached as a linked document and tag it with your security label (security, compliance). Link the ticket to the sprint story that introduced the vulnerability if traceable. Teams using DREAD or risk-register workflows should attach the scoring worksheet to the same ticket.
Common Mistakes Checklist
These are the architectural and implementation anti-patterns that generate the most recurring vulnerabilities in production web applications. Review them before each security sprint or penetration-test scope definition.
Frequently Asked Questions
How do web vulnerability patterns map to OWASP Top 10 and NIST controls?
Each OWASP category (A03 Injection, A07 XSS, A08 CSRF) maps to one or more NIST SP 800-53 controls. A03 maps to SI-10 (Input Validation) and CM-8; A07 maps to SI-10 and SC-18 (Mobile Code); A08 maps to AC-3 and AC-6. Implementing context-aware encoding and parameterised queries simultaneously satisfies the OWASP ASVS V5 requirements and provides the audit evidence NIST auditors expect. The compliance table in the section above lists specific control IDs and required artifacts for each mapping.
What is the difference between SAST and DAST for vulnerability validation?
SAST analyses source code before deployment, catching injection sinks, unsafe deserialisation, and missing security headers at the PR stage — zero runtime required. DAST probes a running application, discovering reflected XSS, SSRF, and open redirect vulnerabilities that only manifest at runtime. Both are required for SOC 2 CC6.6 evidence. Treat them as complementary: SAST has high false-positive rates for business logic; DAST has coverage gaps for authenticated flows.
How often should vulnerability mitigations be reviewed?
Review quarterly or immediately following major framework releases, CVEs affecting your dependency tree, or OWASP ASVS baseline updates. Tie the review cadence to your risk register SLA: CVSS >= 9.0 within 24 hours, >= 7.0 within 14 days, lower within a sprint cycle. Combine CVSS scores with EPSS probability — a CVSS 6.5 finding with EPSS 0.72 deserves the same urgency as a CVSS 8.0.
Can a WAF replace source-level input validation?
No. WAF rules are a defence-in-depth layer, not a substitute. Encoding bypasses (double-URL encoding, Unicode normalisation, HTML entity confusion), zero-day payloads, and application-specific business-logic flaws routinely evade WAF signatures. NIST SI-10 explicitly requires input validation at the application layer regardless of perimeter controls. A WAF that blocks a novel attack buys hours; source-level parameterised queries prevent the entire attack class permanently.
Which SOC 2 trust criteria do web vulnerability mitigations satisfy?
CC6.1 (logical access restricted to authorised entities), CC6.6 (boundary protection and network segmentation), and CC7.2 (system monitoring and anomaly detection) are the primary criteria. Injection prevention and CSRF defence contribute evidence to CC6.1 and CC6.6. Runtime monitoring via RASP and SIEM feeds CC7.2. Audit artefacts include scan SARIF files, CI gate pass/fail logs, and cookie-attribute DAST reports — all retained for 12 months under SOC 2 Type II requirements.
What is EPSS and why use it alongside CVSS?
EPSS (Exploit Prediction Scoring System) estimates the probability that a given CVE will be exploited in the wild within 30 days, derived from observed exploitation telemetry across the internet. CVSS measures theoretical severity, not exploitation likelihood — a CVSS 9.8 finding with EPSS 0.003 may sit unpatched for a sprint cycle without material risk, while a CVSS 6.5 finding with EPSS 0.85 is actively being weaponised. Combining both scores reduces false-priority noise and focuses remediation where real attacker activity is highest.
Related
- Cross-Site Scripting (XSS) Mitigation — context-aware encoding strategies, Trusted Types, and CSP directive configuration for React, Vue, and server-rendered applications.
- Injection Attack Prevention — parameterised queries, ORM binding patterns, and input allowlisting for SQL and NoSQL databases.
- Cross-Site Request Forgery (CSRF) Defence — double-submit cookie implementation,
SameSitecookie strategy, and anti-CSRF patterns for REST and GraphQL endpoints. - Server-Side Request Forgery (SSRF) Prevention — host allowlisting, DNS rebinding protection, and metadata endpoint blocking for server-side HTTP clients.
- Secure HTTP Header Configuration — CSP, HSTS,
X-Frame-Options, andPermissions-Policydeployment patterns for CDN and reverse-proxy layers. - DOM-Based Vulnerability Sanitisation — DOMPurify integration, dangerous sink identification, and client-side sanitisation pipeline architecture.