OWASP ASVS: Application Security Verification Standard Testing Guide

The OWASP Top 10 tells you what categories of vulnerability to worry about. OWASP ASVS tells you exactly which controls your application must implement — and how to verify each one. If you're running a serious security program, you need both.

The Application Security Verification Standard (ASVS) is a catalogue of security requirements that scales from basic hygiene to defence-in-depth. It gives penetration testers a structured checklist, gives developers a concrete specification, and gives security teams a language for measuring maturity over time. This guide covers how to use it in practice: what the levels mean, which control domains matter most, how to test them, and how to integrate ASVS verification into your CI/CD pipeline.

What Is OWASP ASVS?

ASVS is a community-driven framework published by the Open Worldwide Application Security Project. The current release is ASVS 4.0, which defines over 280 individual security requirements organised into 14 control chapters (V1 through V14). Each requirement is tagged with a verification level — L1, L2, or L3 — indicating the depth of assurance required.

Unlike a vulnerability scanner, ASVS is not looking for what is broken. It is verifying that required controls exist and function correctly. A clean Nessus scan does not mean you pass ASVS L1. ASVS asks: does your login page enforce account lockout? Does your session cookie carry Secure and HttpOnly flags? Does your API validate that the authenticated user owns the resource they are requesting? These are controls a vulnerability scanner will not confirm.

The Three Verification Levels

Level Target Assurance Typical Use
L1 — Opportunistic All applications Black-box / automated Baseline hygiene, B2C apps, low-risk internal tools
L2 — Standard Applications handling sensitive data Grey-box, code review SaaS platforms, fintech, healthcare, e-commerce
L3 — Advanced Critical or high-value targets White-box, threat modelling, design review Banking cores, payment processors, critical infrastructure

L1 requirements can be verified entirely from the outside: HTTP responses, cookie attributes, TLS configuration, observable behaviour. L2 requires access to source code or configuration — you need to confirm that bcrypt is actually used for password hashing, not just observe a login form. L3 goes further into architectural review, threat modelling, and validating that security controls are tamper-resistant by design.

Most organisations building web applications should target L2 as their baseline and reserve L3 for systems that process financial transactions or sensitive personal data at scale.

Quick orientation: ASVS chapters V1 (Architecture) and V5 (Validation / Sanitisation / Encoding) apply horizontally across every other chapter. Don't skip them — they contain foundational requirements that underpin everything else.

Key Control Domains

V2 — Authentication

Chapter V2 is often the most consequential section in a web application assessment. It covers password strength, credential storage, multi-factor authentication, lookup secrets, and out-of-band verification. Key L1 requirements include:

Testing V2.1 (password security) from the command line:

# Test minimum password length enforcement
curl -s -o /dev/null -w "%{http_code}" -X POST https://target.example.com/api/register \
  -H "Content-Type: application/json" \
  -d '{"email":"test@example.com","password":"short"}'

# Probe reset token reuse — second request should return 400/401
TOKEN=$(curl -s -X POST https://target.example.com/api/reset-password \
  -d 'token=CAPTURED_TOKEN&password=NewP@ssw0rd123' | jq -r '.token')
curl -s -X POST https://target.example.com/api/reset-password \
  -d "token=$TOKEN&password=AnotherP@ss456"

# Brute-force rate limiting check (expect 429 or lockout)
for i in $(seq 1 110); do
  CODE=$(curl -s -o /dev/null -w "%{http_code}" -X POST https://target.example.com/api/login \
    -H "Content-Type: application/json" \
    -d "{\"email\":\"victim@example.com\",\"password\":\"wrong$i\"}")
  echo "Attempt $i: $CODE"
done

V3 — Session Management

Session management failures are the bridge between authentication bypass and full account takeover. V3 verifies that sessions are properly created, rotated, and invalidated.

# Inspect session cookie attributes
curl -si -X POST https://target.example.com/api/login \
  -H "Content-Type: application/json" \
  -d '{"email":"user@example.com","password":"ValidPass!"}' \
  | grep -i "set-cookie"

# Expected output should include:
# Set-Cookie: session=...; Path=/; HttpOnly; Secure; SameSite=Strict

# Test server-side session invalidation after logout
SESSION=$(curl -si -X POST https://target.example.com/api/login \
  -d '{"email":"user@example.com","password":"ValidPass!"}' \
  | grep -oP 'session=\K[^;]+')

curl -s -X POST https://target.example.com/api/logout \
  -H "Cookie: session=$SESSION"

# Re-use the old session — should return 401
curl -s -o /dev/null -w "%{http_code}" \
  -H "Cookie: session=$SESSION" \
  https://target.example.com/api/profile

V4 — Access Control

Access control is where ASVS diverges most sharply from what automated scanners catch. V4 requires that every request validate not just that the user is authenticated, but that they are authorised to access the specific resource they are requesting. This is the IDOR (Insecure Direct Object Reference) domain.

# IDOR test: access another user's resource with your own token
# First: authenticate as user A
TOKEN_A=$(curl -s -X POST https://target.example.com/api/login \
  -H "Content-Type: application/json" \
  -d '{"email":"usera@example.com","password":"PassA!"}' \
  | jq -r '.token')

# Then: try to retrieve user B's order (ID discovered via enumeration)
curl -s -H "Authorization: Bearer $TOKEN_A" \
  https://target.example.com/api/orders/USER_B_ORDER_ID

# Check for admin panel exposure
for path in /admin /admin/ /administrator /management /console /dashboard/admin; do
  CODE=$(curl -s -o /dev/null -w "%{http_code}" \
    https://target.example.com$path)
  echo "$path → $CODE"
done

V6 — Cryptography

V6 is one of the more L2/L3-heavy chapters because many cryptographic failures are not observable from the outside. You need source code access to confirm that bcrypt with a cost factor of 10+ is used for passwords, that AES-256-GCM (not ECB) is used for at-rest encryption, and that PRNG seeding is cryptographically secure. What you can test from outside:

# Enumerate TLS configuration and cipher suites
nmap --script ssl-enum-ciphers -p 443 target.example.com

# Check for weak ciphers and deprecated protocols (SSLv3, TLS 1.0, TLS 1.1)
testssl.sh --severity HIGH --color 0 https://target.example.com

# Verify HSTS header presence and max-age
curl -si https://target.example.com | grep -i strict-transport

# Check certificate chain
openssl s_client -connect target.example.com:443 -showcerts 2>/dev/null \
  | openssl x509 -noout -dates -subject -issuer

For L2 code review, check that password hashing uses a memory-hard algorithm. In Python:

# Good — bcrypt with adequate cost factor
import bcrypt
hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12))

# Bad — MD5, SHA-1, SHA-256 without salt (ASVS V6.4.1 violation)
import hashlib
hashed = hashlib.md5(password.encode()).hexdigest()

# Also bad — PBKDF2 with fewer than 310,000 iterations (NIST SP 800-132 guidance)

V7 — Error Handling and Logging

Error handling failures leak internal information (stack traces, database errors, file paths) and logging failures mean you have no forensic record when something goes wrong. V7 L1 requirements:

# Trigger error conditions and inspect responses
# SQL injection probe — look for database error messages
curl -s "https://target.example.com/api/product?id=1'" \
  | grep -iE "(mysql|postgresql|oracle|sqlite|syntax error|ORA-|SQLSTATE)"

# Path traversal probe — look for server path disclosure
curl -s "https://target.example.com/api/file?name=../../../../etc/passwd"

# Force a 500 error and check for stack trace leakage
curl -s -X POST https://target.example.com/api/process \
  -H "Content-Type: application/json" \
  -d '{"__proto__":{"polluted":true}}' \
  | grep -iE "(traceback|stack trace|exception|at line|\.py:|\.java:|\.js:)"

V8 — Data Protection

V8 verifies that sensitive data is protected in transit, at rest, and that the application does not cache or expose data unnecessarily. Key L1 checks:

# Check Cache-Control on authenticated responses
curl -si -H "Authorization: Bearer $TOKEN" \
  https://target.example.com/api/profile \
  | grep -i "cache-control"
# Should see: Cache-Control: no-store or no-cache, no-store, must-revalidate

# Inspect browser storage for sensitive data (DevTools console)
# Run in browser console after authentication:
# Object.keys(localStorage).forEach(k => console.log(k, localStorage.getItem(k)));
# Object.keys(sessionStorage).forEach(k => console.log(k, sessionStorage.getItem(k)));

# Check for sensitive data in URL (look for tokens/passwords in GET params)
curl -si "https://target.example.com/reset?token=abc123" -o /dev/null -w "%{redirect_url}"

V9 — Communications

V9 overlaps with V6 cryptography but focuses specifically on transport security: TLS version, certificate validation, HTTP Strict Transport Security, and mixed content. Nuclei has ready-made templates for most of these:

# Run Nuclei against communication security templates
nuclei -u https://target.example.com \
  -t ssl/ \
  -t http/misconfiguration/http-missing-security-headers.yaml \
  -t http/misconfiguration/hsts-missing.yaml \
  -severity medium,high,critical

# Check for HTTP→HTTPS redirect (V9.1.1)
curl -si http://target.example.com/ | grep -i location

# Verify HSTS includeSubDomains and preload
curl -si https://target.example.com | grep -i strict-transport-security
# Should contain: max-age=31536000; includeSubDomains

# Test for mixed content (HTTPS page loading HTTP resources)
# Use browser DevTools Security panel or:
gospider -s https://target.example.com -d 1 --other-source \
  | grep "^http://"

V13 — API and Web Service

V13 is the control chapter most relevant to modern microservice architectures. It addresses REST and GraphQL API security, schema validation, and service-to-service authentication. Key requirements:

# Test Content-Type validation bypass (V13.1.3)
# Many APIs incorrectly process requests even with wrong Content-Type
curl -s -X POST https://target.example.com/api/transfer \
  -H "Content-Type: text/plain" \
  -d '{"amount":1000,"to":"attacker-account"}'

# GraphQL introspection check (V13.4.1 — should be disabled in production)
curl -s -X POST https://target.example.com/graphql \
  -H "Content-Type: application/json" \
  -d '{"query":"{__schema{types{name}}}"}'

# GraphQL depth limit test — deeply nested query
curl -s -X POST https://target.example.com/graphql \
  -H "Content-Type: application/json" \
  -d '{"query":"{user{posts{comments{author{posts{comments{author{name}}}}}}}}"}' \
  | jq '.errors'

# Mass assignment test — send extra fields not expected by the server
curl -s -X PUT https://target.example.com/api/user/profile \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"Attacker","role":"admin","isAdmin":true,"credits":99999}'

V14 — Configuration

V14 covers server configuration: security headers, component security, HTTP methods, and build artefact integrity. This is the most automatable chapter — Nuclei and curl cover 80% of it.

# Full security header audit
curl -si https://target.example.com/ | grep -iE \
  "(x-content-type-options|x-frame-options|content-security-policy|\
referrer-policy|permissions-policy|x-xss-protection|feature-policy)"

# Nuclei template for security header misconfigs
nuclei -u https://target.example.com \
  -t http/misconfiguration/ \
  -t http/vulnerabilities/ \
  -tags headers,config \
  -severity info,low,medium,high,critical

# Check dangerous HTTP methods (V14.5.1)
curl -si -X OPTIONS https://target.example.com/api/ \
  | grep -i allow
# TRACE, CONNECT, PUT, DELETE should not be enabled unless explicitly required

# Verify X-Content-Type-Options (prevents MIME sniffing)
curl -si https://target.example.com/ | grep -i "x-content-type-options"
# Should be: X-Content-Type-Options: nosniff

Mapping ASVS Requirements to Penetration Test Findings

One of ASVS's most practical uses is giving penetration test findings a standardised reference. Instead of a finding titled "Weak Password Policy" with a subjective severity, you can cite the exact requirement that was not met. This makes remediation instructions unambiguous and enables tracking of control coverage over time.

Finding ASVS Reference Level CWE
No account lockout on login V2.2.1 L1 CWE-307
Session token not rotated on login V3.3.1 L1 CWE-384
Missing HttpOnly on session cookie V3.4.2 L1 CWE-1004
IDOR on user resource endpoint V4.2.1 L1 CWE-639
MD5 used for password hashing V6.4.1 L2 CWE-916
Stack trace in error response V7.4.1 L1 CWE-209
Missing Content-Security-Policy header V14.4.3 L1 CWE-1021
GraphQL introspection enabled in production V13.4.1 L2 CWE-200
TLS 1.0 still accepted V9.1.2 L1 CWE-326
Sensitive data in URL parameters V8.3.1 L1 CWE-598

When writing a report, include the ASVS reference alongside CVSS score, CWE, and the specific test that triggered the finding. Many clients now contractually require ASVS L1 or L2 compliance and need the exact reference to track remediation against their compliance programme.

ASVS vs OWASP Top 10 — Complementary, Not Competing

Security professionals sometimes treat ASVS and the OWASP Top 10 as alternatives. They are not — they answer different questions.

Dimension OWASP Top 10 OWASP ASVS
Purpose Awareness — what risks are most common and critical Verification — which controls must be present and working
Format 10 risk categories (A01–A10) 280+ individual requirements across 14 chapters
Audience Developers, management, broad security awareness AppSec engineers, penetration testers, auditors
Testability Describes vulnerability classes, not test procedures Each requirement is verifiable with a specific test
Compliance use Rarely cited in contracts; awareness tool Directly used in procurement, RFPs, audit frameworks

The OWASP Top 10 maps to ASVS categories but is not a subset or superset. A01 (Broken Access Control) aligns with ASVS V4 but ASVS V4 is far more specific — it has 19 individual requirements covering horizontal access control, vertical privilege escalation, directory traversal, and metadata exposure separately. Use the Top 10 to explain risk to stakeholders; use ASVS to build the actual test plan.

ASVS and compliance frameworks: ISO 27001 Annex A.14 (secure development) maps well to ASVS L2. SOC 2 Trust Service Criteria for availability and processing integrity align to ASVS L1 baseline. PCI DSS Requirement 6.3 (security of software) explicitly references OWASP ASVS as an acceptable secure coding framework.

Integrating ASVS into CI/CD and DevSecOps

Static checklists only work if someone runs them. The real value of ASVS in a mature engineering organisation comes from automating L1 verification in CI/CD and surfacing deviations before they reach production.

What You Can Automate in CI/CD

Not all 280+ ASVS requirements are automatable, but a significant subset of L1 is. The categories that map cleanly to automated tools:

# Example: GitHub Actions job for ASVS L1 automated checks
name: ASVS L1 Verification
on:
  deployment_status:

jobs:
  asvs-l1:
    if: github.event.deployment_status.state == 'success'
    runs-on: ubuntu-latest
    steps:
      - name: Check security headers (V14.4)
        run: |
          URL="${{ github.event.deployment_status.environment_url }}"
          HEADERS=$(curl -si "$URL" | grep -iE \
            "(x-content-type-options|x-frame-options|content-security-policy|\
strict-transport-security|referrer-policy)")
          echo "$HEADERS"
          echo "$HEADERS" | grep -qi "x-content-type-options: nosniff" || \
            (echo "FAIL: Missing X-Content-Type-Options" && exit 1)
          echo "$HEADERS" | grep -qi "strict-transport-security" || \
            (echo "FAIL: Missing HSTS header" && exit 1)

      - name: Nuclei scan (V14 misconfig)
        uses: projectdiscovery/nuclei-action@main
        with:
          target: ${{ github.event.deployment_status.environment_url }}
          flags: "-t http/misconfiguration/ -severity high,critical"

      - name: Dependency audit (V6.2 — known vulnerable libs)
        run: npm audit --audit-level=high
        # or: pip-audit --fail-on-finding for Python
        # or: trivy fs . --exit-code 1 --severity HIGH,CRITICAL

Structuring a ASVS-Driven Security Test Plan

For a structured engagement, map your test plan directly to ASVS chapters. A typical L1 assessment for a SaaS web application:

  1. Reconnaissance: Map application surface, identify technology stack, enumerate endpoints (feeds V13, V14)
  2. V14 configuration review: TLS, headers, HTTP methods — fully automatable with Nuclei + testssl.sh
  3. V2 authentication testing: Password policy, lockout, reset flow, credential exposure
  4. V3 session management: Cookie attributes, session rotation, logout invalidation
  5. V4 access control: IDOR probes, privilege escalation, unauthenticated access to authenticated endpoints
  6. V7 error handling: Force errors across all input vectors, review disclosure level
  7. V8 data protection: Sensitive data in URLs, cache headers, client-side storage
  8. V13 API: Content-Type enforcement, mass assignment, API-specific injection
  9. Reporting: Map each finding to ASVS requirement ID, level, and CWE

ASVS Scoring and Gap Analysis

You can build a simple scoring model against ASVS to track maturity over time. For each chapter, count the number of applicable requirements and how many pass verification:

# Simple ASVS scoring spreadsheet structure (export to CSV)
Chapter,Total_Requirements,Tested,Passed,Failed,Not_Applicable,Score_%
V2 Authentication,56,56,44,12,0,78.6
V3 Session Management,21,21,19,2,0,90.5
V4 Access Control,19,19,14,5,0,73.7
V6 Cryptography,49,30,28,2,19,93.3
V7 Error Handling,8,8,6,2,0,75.0
V8 Data Protection,25,20,17,3,5,85.0
V9 Communications,9,9,9,0,0,100.0
V13 API,15,15,10,5,0,66.7
V14 Configuration,14,14,12,2,0,85.7

Run this gap analysis at the start of an engagement and again after remediation. A mature DevSecOps team uses this data to set quarterly security objectives: "Bring V4 Access Control from 73% to 90% this quarter."

Practical ASVS Testing With Nuclei

Nuclei's community template library has grown to cover a substantial slice of ASVS L1. The templates are tagged and can be filtered by category. A focused ASVS-oriented scan:

# Install or update Nuclei templates
nuclei -update-templates

# Run all templates relevant to ASVS L1 configuration and misconfiguration
nuclei -u https://target.example.com \
  -t http/misconfiguration/ \
  -t ssl/ \
  -t http/exposures/ \
  -t http/vulnerabilities/generic/ \
  -severity low,medium,high,critical \
  -o nuclei-asvs-l1.txt \
  -markdown-export ./nuclei-report/

# Targeted: authentication and session issues (V2, V3)
nuclei -u https://target.example.com \
  -tags auth,session,login,password \
  -severity medium,high,critical

# Targeted: API security (V13)
nuclei -u https://target.example.com \
  -tags graphql,api,rest \
  -severity medium,high,critical

# Check for information disclosure (V7)
nuclei -u https://target.example.com \
  -t http/exposures/configs/ \
  -t http/exposures/logs/ \
  -t http/exposures/files/ \
  -severity low,medium,high,critical

Nuclei will not complete a full ASVS verification for you — particularly V4 (access control) and most of V2 require authenticated sessions and manual logic testing. But it handles the automatable 30–40% efficiently and surfaces the obvious L1 gaps before you burn manual testing time.

Tool coverage reality check: Nuclei + testssl.sh + authenticated curl scripts cover approximately 35–45% of ASVS L1 requirements. The remainder requires manual testing, particularly access control logic (V4), authentication flow analysis (V2), and data protection verification (V8). Plan accordingly when scoping engagements.

ASVS for Developers: Security Requirements as Code

For development teams, ASVS is most effective when requirements are embedded in the definition of done rather than discovered in a penetration test six months later. Practical implementation patterns:

The OWASP ASVS project maintains a CSV export of all requirements that can be imported into Jira, Linear, or any issue tracker. Tag L1 requirements as "must have for MVP" and L2 requirements as "must have before production launch with real user data" to give engineering clear prioritisation language.

Common ASVS Assessment Mistakes

After running ASVS-mapped assessments across dozens of applications, the failure modes cluster into a few repeating patterns:

Automate ASVS L1 Verification With Ironimo

Ironimo runs Kali Linux-grade security scans against your web application on demand — covering the automatable slice of ASVS L1: TLS configuration (V9), security headers (V14), API misconfiguration (V13), information disclosure (V7), and more. Get a structured finding report mapped to ASVS requirement IDs, so your remediation backlog is always tied to specific controls.

Run your first ASVS-aligned scan in under five minutes — no agents, no infrastructure changes required.

Start free scan