OWASP Top 10 Testing Checklist: How to Test Each Vulnerability

OWASP Top 10 Testing Checklist: How to Test Each Vulnerability

The OWASP Top 10 is the most referenced list in web application security. Published by the Open Web Application Security Project, it represents the ten most critical security risks based on data from thousands of real-world applications.

This checklist gives you concrete tests for each category — not just definitions, but actual test cases you can run against your application.

How to Use This Checklist

For each category:

  1. Identify where your application is potentially exposed
  2. Run the listed test cases
  3. Verify each remediation is actually in place — don't assume

Mark each item as:

  • ✅ Tested and secure
  • ❌ Vulnerable — needs fix
  • ⏭ Not applicable to this application

A01: Broken Access Control

The #1 risk. Users doing things they shouldn't be able to do.

Test cases:

□ Change your user ID in API requests to another user's ID
  GET /api/users/1001/profile → change to /api/users/1002/profile
  Expected: 403 Forbidden
  Fail: returns user 1002's data

□ Access admin pages while logged in as a regular user
  GET /admin/dashboard
  Expected: 403 or redirect to login
  Fail: admin panel loads

□ Skip steps in a multi-step workflow
  Go directly to /checkout/confirm without completing /checkout/payment
  Expected: redirect back or error
  Fail: order completes without payment

□ Access another user's files by guessing filenames
  GET /files/user-1001/invoice.pdf → try /files/user-1002/invoice.pdf
  Expected: 403
  Fail: file downloads

□ Test force browsing for unlinked admin/debug endpoints
  Try: /admin, /debug, /api-docs, /actuator, /metrics, /.git
  Expected: 404 or 403
  Fail: page loads with sensitive data

□ Modify hidden form fields
  Find a hidden input with a user ID or role — change it before submitting
  Expected: server ignores the field or rejects the request
  Fail: server uses the modified value

What to look for: The server must enforce authorization on every request. Client-side hiding (hiding an admin button in the UI) is not access control.


A02: Cryptographic Failures

Sensitive data exposed due to weak encryption, missing encryption, or implementation errors.

Test cases:

□ Check all traffic is HTTPS
  Open DevTools Network tab — look for any HTTP requests
  Expected: all requests use HTTPS
  Fail: any sensitive data transmitted over HTTP

□ Check HSTS header
  curl -I https://example.com | grep Strict-Transport-Security
  Expected: Strict-Transport-Security: max-age=31536000; includeSubDomains
  Fail: header missing

□ Look for sensitive data in URL parameters
  Search HTTP history for: password=, token=, key=, secret= in URL
  Expected: none
  Fail: sensitive values in URLs (logged by servers, browsers, CDNs)

□ Check for sensitive data in local storage / cookies
  DevTools → Application → Local Storage / Session Storage / Cookies
  Expected: no plaintext passwords or private tokens stored client-side
  Fail: sensitive values visible

□ Check cookie security flags
  Inspect Set-Cookie headers
  Expected: Secure; HttpOnly; SameSite=Strict (or Lax)
  Fail: missing flags (especially Secure and HttpOnly on auth cookies)

□ Check if old SSL/TLS versions are accepted
  testssl.sh example.com
  Expected: only TLS 1.2+ accepted
  Fail: TLS 1.0/1.1 or SSL accepted

□ Check for secrets in client-side JS
  View page source and JS bundles — search for: api_key, secret, password, token
  Expected: none
  Fail: credentials hardcoded in source

A03: Injection

Untrusted data sent to an interpreter — SQL, OS commands, LDAP, etc.

Test cases:

□ SQL Injection — basic
  Append ' to any parameter: ?id=1'
  Expected: normal response or generic error
  Fail: database error message visible

□ SQL Injection — boolean-based
  ?id=1 AND 1=1--   vs   ?id=1 AND 1=2--
  Expected: same response
  Fail: different responses indicate injectable parameter

□ SQL Injection — automated
  sqlmap -u "https://example.com/items?id=1" --level=2
  Expected: no injections found
  Fail: sqlmap extracts database content

□ Command Injection
  In any input that might reach the OS (file name, IP address field, etc.):
  test; whoami
  test && id
  test | ls
  Expected: no command output in response
  Fail: command output visible

□ LDAP Injection (if LDAP auth is used)
  Username: admin)(|(password=*)
  Expected: authentication fails
  Fail: bypass or LDAP error visible

□ XPath Injection (if XML used internally)
  ' or '1'='1
  Expected: normal behavior
  Fail: query returns unexpected results or error

□ Template Injection
  In any template-rendered input (name fields, email templates, etc.):
  {{7*7}}   or   ${7*7}   or   <%= 7*7 %>
  Expected: literal text rendered, not 49
  Fail: 49 appears — template is evaluating input

A04: Insecure Design

Missing security controls in the application architecture itself.

Test cases:

□ Check password reset flow
  Request reset → check if token is predictable (sequential, timestamp-based)
  Expected: high-entropy random token
  Fail: predictable pattern

□ Check token expiry
  Request a password reset token → wait 24 hours → try to use it
  Expected: token expired / invalid
  Fail: old token still works

□ Check one-time token reuse
  Use a password reset link → try to use it again
  Expected: link is invalid after first use
  Fail: link works multiple times

□ Account enumeration on password reset
  Enter a registered email → note response message
  Enter a non-registered email → note response message
  Expected: identical responses
  Fail: different responses reveal whether email is registered

□ Check rate limiting on sensitive actions
  Attempt login 50 times rapidly
  Expected: lockout, CAPTCHA, or progressive delay after N failures
  Fail: unlimited attempts allowed

□ Business logic — skip workflow steps
  Multi-step checkout: go directly to confirmation step URL
  Expected: server validates previous steps completed
  Fail: order completes without payment

A05: Security Misconfiguration

Insecure default configurations, incomplete setup, open cloud storage, verbose errors.

Test cases:

□ Check for verbose error messages
  Cause an error (wrong type parameter, missing required field)
  Expected: generic error message ("Something went wrong")
  Fail: stack traces, file paths, or database errors visible

□ Check for default credentials
  Try: admin/admin, admin/password, root/root, test/test
  Expected: all fail
  Fail: any default credential works

□ Check security headers
  curl -I https://example.com
  Expected headers: Content-Security-Policy, X-Frame-Options,
    X-Content-Type-Options, Referrer-Policy, Permissions-Policy
  Fail: missing security headers

□ Check for exposed admin interfaces
  Try: /admin, /phpmyadmin, /adminer, /.env, /config.php
  Expected: 403 or 404
  Fail: admin interfaces publicly accessible

□ Check for exposed .git directory
  curl https://example.com/.git/HEAD
  Expected: 404
  Fail: "ref: refs/heads/main" returned — source code accessible

□ Check CORS configuration
  curl -H "Origin: https://evil.com" -I https://example.com/api/data
  Expected: no Access-Control-Allow-Origin: * on sensitive endpoints
  Fail: wildcard CORS allows any origin to read your API

□ Check for directory listing
  Navigate to a directory (e.g., /uploads/)
  Expected: 403 or 404
  Fail: file listing displayed

A06: Vulnerable and Outdated Components

Using libraries, frameworks, or components with known vulnerabilities.

Test cases:

□ Scan dependencies for known vulnerabilities
  npm audit --audit-level=moderate
  pip-audit
  bundler-audit
  Expected: no critical/high vulnerabilities
  Fail: unpatched CVEs in dependencies

□ Check for outdated server software in response headers
  curl -I https://example.com | grep -i server
  Check version against CVE database
  Expected: either no version exposed or current version
  Fail: old versions with known CVEs

□ Check WordPress/CMS plugin versions (if applicable)
  wpscan --url https://example.com
  Expected: all plugins and themes up to date
  Fail: outdated plugins with known exploits

□ Check for abandoned packages
  npm outdated → look for packages with no recent updates
  Research if abandoned packages have unpatched security issues

□ Run automated scanner against known vulnerability signatures
  nuclei -u https://example.com -t nuclei-templates/cves/
  Expected: no CVE matches
  Fail: known CVEs detected

A07: Identification and Authentication Failures

Broken authentication, weak passwords, session management flaws.

Test cases:

□ Check password policy
  Create account with: password123, 12345678, qwerty, abc123
  Expected: all rejected as too weak
  Fail: weak passwords accepted

□ Check against known breached passwords
  Try: "password", "123456", "letmein" (top 10 breached passwords)
  Expected: rejected
  Fail: common passwords accepted

□ Check brute force protection on login
  Submit 20 wrong passwords rapidly
  Expected: lockout or CAPTCHA after N failures
  Fail: unlimited attempts

□ Check session token entropy
  Log in 10 times → collect all session tokens
  Look for patterns (sequential, timestamp-based, base64-encoded user data)
  Expected: high-entropy random tokens, no patterns
  Fail: predictable tokens

□ Check session invalidation on logout
  Log in → capture session token → log out → use the old token
  Expected: 401 Unauthorized
  Fail: old session still works

□ Check session invalidation on password change
  Log in on two devices → change password on device 1 → try to use device 2's session
  Expected: device 2 logged out
  Fail: old session still active after password change

□ Check "Remember Me" implementation
  Enable remember me → inspect the persistent cookie
  Expected: opaque random token stored server-side
  Fail: user ID or other identifying info encoded in cookie

□ Multi-factor authentication bypass
  If MFA is available: complete step 1 (password), skip to authenticated URL
  Expected: redirected back to MFA prompt
  Fail: MFA bypassed

A08: Software and Data Integrity Failures

Code and infrastructure that doesn't verify integrity — insecure deserialization, compromised CI/CD.

Test cases:

□ Check for insecure deserialization
  Find any endpoint that accepts serialized data (look for base64-encoded data,
  Java serialized objects starting with "rO0", pickle data)
  Attempt deserialization gadget chain attacks
  Expected: input rejected or handled safely
  Fail: code execution or unexpected behavior

□ Check auto-update mechanisms
  Does the app auto-update without signature verification?
  Expected: updates verified with cryptographic signature
  Fail: updates downloaded and executed without verification

□ Inspect CI/CD pipeline for supply chain risks
  Are third-party actions pinned by hash in GitHub Actions?
  Expected: uses: actions/checkout@v4 with SHA pin
  Fail: uses: some-third-party/action@latest (mutable reference)

□ Check Content Security Policy for unsafe-inline
  curl -I https://example.com | grep Content-Security-Policy
  Expected: no unsafe-inline or unsafe-eval directives
  Fail: CSP allows inline scripts (XSS mitigation bypassed)

A09: Security Logging and Monitoring Failures

Not detecting, escalating, or alerting on active attacks.

Test cases:

□ Check if login failures are logged
  Attempt 10 failed logins → check if security team gets alerted
  Expected: alerts triggered, attempts logged
  Fail: no logging or alerting

□ Check if admin actions are audited
  As admin: delete a user, change permissions, export data
  Expected: all actions logged with user, timestamp, and action
  Fail: no audit trail

□ Check log content for sensitive data
  Review logs — do they contain plaintext passwords, credit cards, or PII?
  Expected: sensitive data masked or excluded from logs
  Fail: raw passwords or PII in log files

□ Verify logs can't be tampered with
  Can a regular user (or attacker with limited access) modify log files?
  Expected: logs written to append-only store, separate from app
  Fail: logs on same filesystem, writable by app process

□ Check for alerting on impossible travel / anomalous logins
  Log in from location A, then immediately from location B (different IP)
  Expected: alert or challenge triggered
  Fail: no detection of anomalous behavior

A10: Server-Side Request Forgery (SSRF)

Tricking the server into making HTTP requests to internal systems.

Test cases:

□ Test URL parameters that fetch external content
  Find features like: link preview, webhook URL, image import by URL, PDF generation
  Inject internal URLs:
    http://localhost/
    http://127.0.0.1/admin
    http://169.254.169.254/latest/meta-data/ (AWS metadata)
    http://10.0.0.1/
  Expected: request blocked or whitelisted domains only
  Fail: response contains internal system content

□ Test for blind SSRF
  Use a callback URL you control (Burp Collaborator, webhook.site):
  https://your-callback.burpcollaborator.net/
  Expected: no outbound request from server
  Fail: server makes request to your callback URL

□ Check URL parsing edge cases
  http://evil.com@internal.example.com/
  http://internal.example.com#@evil.com/
  Expected: all treated correctly (no bypass)
  Fail: URL validation bypassed, internal request made

□ Test DNS rebinding (advanced)
  If URL validation is done at request time, check if it's also done at connection time
  Expected: validation not bypassable via DNS rebinding
  Fail: SSRF possible through DNS rebinding

Keeping the Checklist Current

OWASP updates the Top 10 periodically (current version: 2021). The categories shift as the threat landscape changes. Keep testing:

  • After every major feature addition
  • Before every production release
  • As part of your CI/CD pipeline (automated checks for A06, A05)
  • During dedicated security review sprints (manual checks for all 10)

For automation, see our guide on automated security testing in CI pipelines. For a deeper dive on tooling, see the Burp Suite tutorial for QA teams.

HelpMeTest can help you run continuous functional tests that cover authentication, authorization, and session management scenarios — catching regressions before they become security incidents.

Read more

Start now free