Web Application Security Checklist for Developers
This is a practical, review-ready checklist mapped to OWASP ASVS 4.0.3 verification levels and the WSTG test IDs your pentester will actually use. Each item is a concrete control you can implement, diff, and test in CI — not a slogan. Work top to bottom; treat every unchecked box as an open finding.
Authentication & Session Management
Authentication failures map to OWASP Top 10 A07:2021 and ASVS chapters V2 (Authentication) and V3 (Session Management). The goal is to make credential theft, brute force, and session fixation structurally impossible rather than merely discouraged. Verify each check against WSTG-ATHN and WSTG-SESS.
- Store passwords with a memory-hard KDF: Argon2id (≥19 MiB, t=2, p=1) or bcrypt cost ≥10 / scrypt — never MD5, SHA-1, or unsalted SHA-256 (ASVS V2.4).
- Enforce a minimum length of 12 characters, allow the full Unicode range and spaces, and screen new passwords against a breached-password list (HIBP k-anonymity range API) — do not impose composition rules or forced rotation (ASVS V2.1, NIST SP 800-63B).
- Rate-limit and exponentially back off authentication attempts per account and per source IP; return identical responses and timing for valid and invalid usernames to prevent user enumeration (WSTG-ATHN-03).
- Generate session IDs with a CSPRNG (≥128 bits entropy), and regenerate the session ID on every privilege change — login, logout, and step-up — to kill session fixation (WSTG-SESS-03).
- Set session cookies HttpOnly, Secure, and SameSite=Lax or Strict; scope them with the __Host- prefix and no explicit Domain attribute (ASVS V3.4).
- Enforce both an idle timeout and an absolute session lifetime server-side; invalidate the session token on the server at logout, not just client-side.
- Offer phishing-resistant MFA — WebAuthn/FIDO2 or TOTP — and never SMS as the only second factor; require MFA re-prompt before sensitive actions (ASVS V2.2, V2.7).
- Make password-reset tokens single-use, short-lived (≤1 hour), random, and delivered out-of-band; invalidate all active sessions on password change (WSTG-ATHN-09).
Access Control & Authorization
Broken access control is A01:2021 — the number one risk. Most of these bugs are logic flaws no scanner catches, so they demand explicit, server-side enforcement and per-object checks. Test against WSTG-ATHZ (IDOR, privilege escalation, path traversal).
- Enforce authorization on the server for every request; deny by default and never rely on a hidden UI element, disabled button, or client-side role check as a control.
- Verify object-level ownership on every access — resolve the resource, then confirm the authenticated principal may act on that specific ID — to prevent IDOR / BOLA (WSTG-ATHZ-04).
- Use unpredictable or indirect object references (UUIDv4 or per-user mapping), but treat this as defense-in-depth, not a substitute for the ownership check above.
- Centralize authorization in one middleware/policy layer (e.g., ABAC/RBAC engine) rather than scattering ad-hoc if-statements across controllers.
- Deny privilege escalation paths: verify a user cannot set their own role, tenant_id, or is_admin flag via mass-assignment on create/update endpoints (WSTG-ATHZ-02).
- Enforce CSRF protection on all state-changing requests — synchronizer token or SameSite cookies plus origin/referer validation for cross-site-sensitive actions (WSTG-SESS-05).
- Validate and canonicalize file paths and imported URLs to block directory traversal (../) and SSRF against internal metadata endpoints like 169.254.169.254 (WSTG-ATHZ-01, A10:2021).
Input Validation & Output Encoding
Injection is A03:2021. The durable fix is separation of code and data at every interpreter boundary — SQL, OS shell, LDAP, XML, and the browser DOM. Validate input for intent, but rely on context-aware output encoding and parameterization for the actual defense (WSTG-INPV).
- Use parameterized queries / prepared statements or a vetted ORM for all database access; never build SQL by string concatenation, and validate any dynamic identifier (table/column) against an allowlist (WSTG-INPV-05).
- Validate all input against a positive allowlist — type, length, format, and range — at the trust boundary; reject rather than sanitize where possible (ASVS V5.1).
- Apply context-aware output encoding for XSS defense: HTML-entity encoding in HTML body, attribute encoding in attributes, and JavaScript/URL encoding in their contexts — prefer auto-escaping template engines (WSTG-CLNT-01).
- Avoid OS command execution with user input; if unavoidable, use argument-array APIs (execFile, not shell=True) and never pass data through a shell interpreter (WSTG-INPV-12).
- Disable XML external entity resolution (XXE) in every XML/SOAP/SVG parser — set FEATURE_SECURE_PROCESSING and disallow DOCTYPE (A05:2021, WSTG-INPV-07).
- Reject unsafe deserialization: never deserialize untrusted data into arbitrary types; use a schema-constrained format like JSON with explicit field mapping (A08:2021).
- Enforce server-side file-upload controls: verify content type by magic bytes, cap size, store outside the web root, and serve with Content-Disposition: attachment.
- Validate redirect targets against an allowlist to prevent open redirects used in phishing (WSTG-CLNT-04).
Security Headers (CSP, HSTS & More)
HTTP response headers are cheap, high-leverage defense-in-depth against XSS, clickjacking, and protocol downgrade. Missing headers fall under A05:2021 Security Misconfiguration; verify with WSTG-CONF-07 and a scanner like securityheaders.com or Mozilla Observatory.
- Deploy a strict Content-Security-Policy: prefer nonce- or hash-based script-src with 'strict-dynamic', set object-src 'none' and base-uri 'none', and avoid 'unsafe-inline'/'unsafe-eline' — roll out via Content-Security-Policy-Report-Only first.
- Set Strict-Transport-Security: max-age=31536000; includeSubDomains; preload and submit the domain to the HSTS preload list to block SSL-stripping.
- Send X-Content-Type-Options: nosniff on every response to stop MIME sniffing.
- Prevent clickjacking with frame-ancestors 'none' (or an explicit allowlist) in CSP; keep X-Frame-Options: DENY as a legacy fallback.
- Set Referrer-Policy: strict-origin-when-cross-origin to avoid leaking full URLs (and tokens in them) to third parties.
- Lock down browser features with a Permissions-Policy (e.g., geolocation=(), camera=(), microphone=()) and set Cross-Origin-Opener-Policy: same-origin.
- Configure CORS explicitly: reflect only vetted origins, never combine Access-Control-Allow-Origin: * with Allow-Credentials: true, and validate the Origin header server-side (WSTG-CLNT-07).
Dependency & Supply-Chain Security
Vulnerable and outdated components are A06:2021; the SolarWinds and Log4Shell classes of incident live here. The discipline is knowing exactly what you ship, verifying its integrity, and patching on a clock. Align with SLSA and NIST SSDF.
- Run automated SCA (Dependabot, npm audit, OWASP Dependency-Check, Trivy) in CI and fail the build on known-exploitable (KEV-listed) or high-severity CVEs.
- Pin dependencies to exact versions and commit a lockfile (package-lock.json, poetry.lock, go.sum) so builds are reproducible and hash-verified.
- Generate and retain an SBOM (CycloneDX or SPDX) for every release so you can answer 'are we affected?' within minutes of a new CVE.
- Vet transitive dependencies and watch for typosquatting/dependency-confusion; scope internal package names and configure your registry to prefer the private index.
- Verify artifact integrity with signatures (Sigstore/cosign) and pin third-party GitHub Actions to a full commit SHA, not a mutable tag.
- Subscribe to advisories for your core frameworks and define an SLA — e.g., critical CVEs patched within 72 hours, high within 7 days.
- Strip unused dependencies and dead features; every package you don't ship is attack surface you don't defend.
Secrets Management
Hard-coded credentials are a perennial finding (CWE-798) and a component of A05:2021. Secrets should never live in source, images, or logs; they should be injected at runtime, rotated, and scoped to least privilege. Verify against ASVS V6 (Stored Cryptography) and V2.10 (service credentials).
- Keep secrets out of source control; scan the repo and its full git history with gitleaks or trufflehog, and enforce a pre-commit / CI secret scanner as a merge gate.
- Store secrets in a dedicated manager (HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager) or the platform's encrypted store — inject via environment or a mounted volume at runtime, never bake them into container images.
- Rotate credentials on a schedule and immediately on suspected exposure; prefer short-lived, dynamically issued credentials (Vault dynamic secrets, cloud IAM roles / OIDC federation) over long-lived static keys.
- Scope each secret to least privilege — a per-service database user with only the grants it needs — so one leak does not compromise the whole system.
- Never log secrets, tokens, or full authorization headers; add redaction filters to your logging pipeline and verify they work.
- Encrypt secrets at rest with a managed KMS and keep the key material out of the same trust boundary as the data it protects.
Logging, Monitoring & Detection
Security logging and monitoring failures are A09:2021 — you cannot respond to what you cannot see. Log security-relevant events in a structured, tamper-evident way and wire them to alerting. Verify against ASVS V7 and WSTG-derived attack signatures.
- Log all authentication events (success and failure), access-control denials, input-validation rejections, and admin actions — with who, what, when, and source, in structured JSON (ASVS V7.1).
- Never log sensitive data — passwords, session tokens, full card numbers, secrets — and neutralize log-injection by encoding untrusted values (CWE-117).
- Ship logs off-host to a central, append-only store (SIEM) so an attacker who owns the box cannot erase their tracks; retain per your compliance requirement.
- Alert on security signals in near-real-time: brute-force spikes, authorization-failure bursts, new-admin creation, and known attack patterns — with an on-call path.
- Include a correlation/request ID across services so an incident can be reconstructed end-to-end.
- Test your detection: run the attack (or a purple-team exercise) and confirm the event actually fires an alert — untested logging is theater.
- Synchronize clocks (NTP) and record timestamps in UTC so cross-system timelines are reliable during forensics.
TLS & Transport Security
Cryptographic failures are A02:2021. All data in transit must be encrypted with modern, correctly-configured TLS; a valid cert is necessary but not sufficient. Test with WSTG-CRYP-01, testssl.sh, and SSL Labs, aiming for an A+ grade.
- Serve everything over HTTPS and redirect HTTP to HTTPS; combined with HSTS preload, allow no plaintext fallback.
- Support only TLS 1.2 and TLS 1.3; disable SSLv3, TLS 1.0, and TLS 1.1, and remove export/NULL/RC4/3DES cipher suites (WSTG-CRYP-01).
- Prefer AEAD cipher suites (AES-GCM, ChaCha20-Poly1305) and enable forward secrecy via ECDHE key exchange only.
- Use certificates from a trusted CA with a ≥2048-bit RSA or ECDSA P-256 key; automate issuance and renewal (ACME/Let's Encrypt) so certs never silently expire.
- Enable OCSP stapling and consider CAA DNS records to constrain which CAs may issue for your domain.
- Terminate TLS for internal service-to-service traffic too (mTLS where practical) — do not treat the internal network as trusted.
- For native/mobile clients, consider certificate pinning, and always validate the full chain and hostname — never disable verification 'to make it work'.
Key takeaways
- ›Enforce every access-control and authorization decision on the server, per object — broken access control (A01) is the top risk and no scanner finds these logic flaws for you.
- ›Beat injection (A03) with parameterized queries and context-aware output encoding, not blocklist filtering.
- ›Ship a strict, nonce-based CSP plus HSTS preload, X-Content-Type-Options, and frame-ancestors — cheap headers that blunt XSS, downgrade, and clickjacking.
- ›Keep secrets out of source, rotate them, and prefer short-lived dynamically-issued credentials over static keys.
- ›You cannot respond to what you do not log: structured, tamper-evident security logging with tested alerting closes A09.
- ›Map your work to OWASP ASVS levels and WSTG test IDs so 'secure' becomes verifiable, not aspirational.
FAQ
What is the difference between OWASP ASVS and the OWASP Top 10?+
The Top 10 is an awareness document ranking the ten most critical risk categories (e.g., A01 Broken Access Control). ASVS is a detailed, testable verification standard with hundreds of specific requirements across three assurance levels (L1–L3). Use the Top 10 to communicate risk and ASVS as your actual engineering checklist.
Which ASVS level should my application target?+
Level 1 is the minimum for any application and is largely testable by black-box means. Level 2 is the standard for applications handling sensitive data — most business apps should target L2. Level 3 is for the highest-assurance systems (payments, health, critical infrastructure) and requires deep design review.
Can a checklist replace a penetration test?+
No. A checklist prevents known classes of defect and makes you review-ready, but it cannot find business-logic flaws, chained exploits, or environment-specific misconfigurations. Use the checklist to harden continuously in CI, and a pentest to independently validate that the controls actually hold under attack.
How often should I run dependency scanning?+
Continuously. Wire SCA (Dependabot, Trivy, OWASP Dependency-Check) into every CI run so new code is checked on commit, and also run scheduled scans of your main branches so newly-disclosed CVEs in already-merged dependencies are caught even when nobody is deploying.
Want this checklist verified against your live application? MonMyIP runs a structured ASVS/WSTG-grounded assessment and delivers a prioritized, evidence-backed remediation report. Book a scoped web application pentest.