“Make the application secure” is not a task anyone can finish. It has no definition of done, no way to show a client what progress looks like, and no way to tell whether last quarter’s work still holds. The OWASP Application Security Verification Standard exists to fix exactly that. It replaces a vague goal with a numbered list of statements that are either true of your application or not.
This is the first part of a series that works through every Level 1 requirement in ASVS 5.0.0, one chapter per article, with each control built twice: once in Node.js with Fastify, once in Go with Fiber. This part is the map. It explains what ASVS is, how the three levels differ, and what Level 1 asks for. Then it lists all 70 Level 1 requirements in OWASP’s own words, with a short explanation of each one underneath.
It is written for developers, DevOps engineers, and sysadmins who have been handed a security requirement, an audit, or a customer questionnaire, and need to know what is actually being asked of them. No security certification is assumed.
What ASVS Is
ASVS stands for Application Security Verification Standard. It is a free, open list of security requirements for web applications and APIs, published by OWASP, the Open Worldwide Application Security Project.
The word verification is the important one. Every entry is written as a statement that a reviewer can check and mark as passed or failed, which is why they all begin with “Verify that”. ASVS does not tell you how to build a login form. It tells you what has to be true about the login form you already built.
Requirements are addressed by a three-part ID:
- A chapter is a broad topic, written
V6. Example:V6 Authentication. - A section groups related requirements inside a chapter, written
V6.2. Example:V6.2 Password Security. - A requirement is one verifiable statement, written
V6.2.1.
That last ID is what you put in a ticket, a pull request, or a report. Version 5.0.0 contains 345 requirements across 17 chapters.
The standard itself lives at github.com/OWASP/ASVS. This series quotes release 5.0.0, git tag v5.0.0, and every requirement on this page is reproduced from it word for word. Whenever you cite ASVS, cite a version with it. The project keeps working between releases, and requirement wording, numbering, and levels all move. “We meet ASVS 5.0.0 V6.2.1” is a claim someone can still check in two years. “We meet ASVS” is not.
How the Three Levels Work
Every requirement carries a level from 1 to 3, and the levels are cumulative. A Level 2 application must satisfy all of Level 1 as well as its own additions, and Level 3 covers everything.
- Level 1 is 70 requirements. The baseline that applies to essentially every application, whatever it does.
- Level 2 adds 183 more, for a running total of 253. This is where most applications handling personal or business data belong.
- Level 3 adds the final 92, for all 345. It targets applications where a breach is catastrophic: medical, financial, critical infrastructure.
Level 1 is a floor, not a finish line, and ASVS says as much itself. Starting there anyway is a practical decision rather than a lowering of standards. 70 requirements is a scope a small team can genuinely close in a few weeks, and none of that work is thrown away later, because every Level 1 requirement is still required at Level 2 and Level 3.
One thing to know before reading the list: a few requirements deliberately scope themselves down at Level 1 and widen higher up. V2.2.1 asks for input validation, then adds “For L1, this can focus on input which is used to make specific business or security decisions.” V5.2.2 does the same for file type checking, and V3.4.1 only requires the HSTS policy to cover subdomains from Level 2. Read a requirement to the end before estimating how much work it is.
What Level 1 Covers
The 70 requirements are spread unevenly across 15 chapters:
| Chapter | Requirements | Covered in |
|---|---|---|
| V1 Encoding and Sanitization | 8 | Part 2 |
| V2 Validation and Business Logic | 4 | Part 3 |
| V3 Web Frontend Security | 8 | Part 4 |
| V4 API and Web Service | 2 | Part 5 |
| V5 File Handling | 4 | Part 6 |
| V6 Authentication | 13 | Parts 7 and 8 |
| V7 Session Management | 6 | Part 9 |
| V8 Authorization | 4 | Part 10 |
| V9 Self-contained Tokens | 4 | Part 11 |
| V10 OAuth and OIDC | 5 | Part 12 |
| V11 Cryptography | 3 | Part 13 |
| V12 Secure Communication | 3 | Part 14 |
| V13 Configuration | 1 | Part 15 |
| V14 Data Protection | 2 | Part 16 |
| V15 Secure Coding and Architecture | 3 | Part 17 |
Two of the seventeen chapters are missing from that table. V16 Security Logging and Error Handling and V17 WebRTC contain no Level 1 requirements at all, because they start at Level 2. That is why this series covers 15 chapters rather than 17. V6 Authentication is split across two parts, because thirteen requirements is too many for one.
Notice how lopsided the distribution is. V6 Authentication alone is 13 requirements, nearly a fifth of the whole list, while V13 Configuration is a single one. Budget your time against this table rather than assuming each chapter is a similar amount of work.
Sorted by the kind of work rather than by topic, Level 1 splits three ways:
- 62 requirements are code changes. Parameterized queries, output encoding, authorization checks, token validation. The bulk of the list, and the bulk of this series.
- 4 are configuration.
V12.1.1,V12.2.1, andV12.2.2are TLS settings, andV13.4.1is about what your deployment carries. All four are reverse proxy and pipeline work that never touches application code. - 4 are documentation.
V2.1.1,V6.1.1,V8.1.1, andV15.1.1ask you to write a policy down. There is no way to pass them by changing code.
Do the documentation four first. Not because paperwork is enjoyable, but because two later requirements are verified against them: V6.3.1 asks whether your brute force controls match your documentation, and V15.2.1 asks whether your dependencies are inside the time frames you defined. Neither can be passed until the document exists.
How to Read a Requirement
Every requirement below appears with OWASP’s exact sentence first, then one line underneath saying what it means in practice. The quoted sentence is the standard and is the thing you cite. The line beneath it is this site’s explanation and carries no authority of its own.
“Verify that” is addressed to whoever is doing the checking, which is often you. Passing a requirement means you can point at something concrete: a line of code, a configuration file, a test, or a command whose output you kept. “We probably do that” is a fail.
“Not applicable” is a legitimate outcome, but only with a reason and a date recorded next to it. If your application accepts no file uploads, the four requirements in V5 File Handling do not apply to you. If you cannot write a sentence a reviewer would accept, the requirement does apply, and you have just found real work.
Be conservative about it. “The application accepts no file uploads of any kind, confirmed by reviewing every multipart route on 2026-08-16” is verifiable. “We do not think anyone would attack that endpoint” is not.
The 70 Level 1 Requirements
V1 Encoding and Sanitization (Part 2)
Eight requirements, and every one is the same idea: data has to be encoded or parameterized for the exact place it is about to land.
V1.2.1 Verify that output encoding for an HTTP response, HTML document, or XML document is relevant for the context required, such as encoding the relevant characters for HTML elements, HTML attributes, HTML comments, CSS, or HTTP header fields, to avoid changing the message or document structure.
Encode for the exact place the value lands. HTML escaping is not enough inside an attribute, a script block, or a header.
V1.2.2 Verify that when dynamically building URLs, untrusted data is encoded according to its context (e.g., URL encoding or base64url encoding for query or path parameters). Ensure that only safe URL protocols are permitted (e.g., disallow javascript: or data:).
Build URLs with a URL library, and refuse any scheme that is not http or https.
V1.2.3 Verify that output encoding or escaping is used when dynamically building JavaScript content (including JSON), to avoid changing the message or document structure (to avoid JavaScript and JSON injection).
Never paste a value straight into a script block. Serialize it, and escape the characters that could close the tag.
V1.2.4 Verify that data selection or database queries (e.g., SQL, HQL, NoSQL, Cypher) use parameterized queries, ORMs, entity frameworks, or are otherwise protected from SQL Injection and other database injection attacks. This is also relevant when writing stored procedures.
No string concatenation in queries, ever. The value travels beside the query, not inside it.
V1.2.5 Verify that the application protects against OS command injection and that operating system calls use parameterized OS queries or use contextual command line output encoding.
Pass arguments as a list. The moment a shell parses your string, a filename can run commands.
V1.3.1 Verify that all untrusted HTML input from WYSIWYG editors or similar is sanitized using a well-known and secure HTML sanitization library or framework feature.
A regular expression that strips tags will be bypassed. Use a library that parses the document and rebuilds it from an allowlist.
V1.3.2 Verify that the application avoids the use of eval() or other dynamic code execution features such as Spring Expression Language (SpEL). Where there is no alternative, any user input being included must be sanitized before being executed.
Search the codebase for eval and new Function, and replace each one with a lookup of allowed operations.
V1.5.1 Verify that the application configures XML parsers to use a restrictive configuration and that unsafe features such as resolving external entities are disabled to prevent XML eXternal Entity (XXE) attacks.
Turn off entity resolution in the XML parser. Otherwise a document can ask your server to read its own files.
V2 Validation and Business Logic (Part 3)
Four requirements about deciding what your application accepts, and enforcing that decision somewhere the user cannot reach.
V2.1.1 Verify that the application’s documentation defines input validation rules for how to check the validity of data items against an expected structure. This could be common data formats such as credit card numbers, email addresses, telephone numbers, or it could be an internal data format.
Write down the accepted shape of every field that matters, so the code can later be checked against something.
V2.2.1 Verify that input is validated to enforce business or functional expectations for that input. This should either use positive validation against an allow list of values, patterns, and ranges, or be based on comparing the input to an expected structure and logical limits according to predefined rules. For L1, this can focus on input which is used to make specific business or security decisions. For L2 and up, this should apply to all input.
List what you accept and reject the rest. A list of known-bad values is always one bypass short.
V2.2.2 Verify that the application is designed to enforce input validation at a trusted service layer. While client-side validation improves usability and should be encouraged, it must not be relied upon as a security control.
Checks in the browser are there to help users. Every one of them has to exist again on the server.
V2.3.1 Verify that the application will only process business logic flows for the same user in the expected sequential step order and without skipping steps.
Record where a user is in a multi-step flow on the server, so step three cannot be called before step two.
V3 Web Frontend Security (Part 4)
Eight requirements, mostly headers and cookie attributes, plus the rules that stop another site driving your application through a logged-in browser.
V3.2.1 Verify that security controls are in place to prevent browsers from rendering content or functionality in HTTP responses in an incorrect context (e.g., when an API, a user-uploaded file or other resource is requested directly). Possible controls could include: not serving the content unless HTTP request header fields (such as Sec-Fetch-*) indicate it is the correct context, using the sandbox directive of the Content-Security-Policy header field or using the attachment disposition type in the Content-Disposition header field.
Serve uploads and API responses so a browser downloads them instead of rendering them as a page.
V3.2.2 Verify that content intended to be displayed as text, rather than rendered as HTML, is handled using safe rendering functions (such as createTextNode or textContent) to prevent unintended execution of content such as HTML or JavaScript.
Use textContent rather than innerHTML. Identical output for real text, no execution for hostile text.
V3.3.1 Verify that cookies have the ‘Secure’ attribute set, and if the ‘__Host-’ prefix is not used for the cookie name, the ‘__Secure-’ prefix must be used for the cookie name.
Add Secure and the __Host- name prefix, so the cookie cannot be set over plain HTTP or by a sibling domain.
V3.4.1 Verify that a Strict-Transport-Security header field is included on all responses to enforce an HTTP Strict Transport Security (HSTS) policy. A maximum age of at least 1 year must be defined, and for L2 and up, the policy must apply to all subdomains as well.
One header with a max-age of a year or more, sent on every response rather than only the home page.
V3.4.2 Verify that the Cross-Origin Resource Sharing (CORS) Access-Control-Allow-Origin header field is a fixed value by the application, or if the Origin HTTP request header field value is used, it is validated against an allowlist of trusted origins. When ‘Access-Control-Allow-Origin: *’ needs to be used, verify that the response does not include any sensitive information.
Compare the Origin value against a fixed list. Echoing back whatever the browser sent allows every site on the internet.
V3.5.1 Verify that, if the application does not rely on the CORS preflight mechanism to prevent disallowed cross-origin requests to use sensitive functionality, these requests are validated to ensure they originate from the application itself. This may be done by using and validating anti-forgery tokens or requiring extra HTTP header fields that are not CORS-safelisted request-header fields. This is to defend against browser-based request forgery attacks, commonly known as cross-site request forgery (CSRF).
A state-changing request needs proof it came from your own pages: an anti-forgery token, or a header no simple form can set.
V3.5.2 Verify that, if the application relies on the CORS preflight mechanism to prevent disallowed cross-origin use of sensitive functionality, it is not possible to call the functionality with a request which does not trigger a CORS-preflight request. This may require checking the values of the ‘Origin’ and ‘Content-Type’ request header fields or using an extra header field that is not a CORS-safelisted header-field.
If preflight is what protects an endpoint, make sure the endpoint cannot be reached by a request that skips preflight.
V3.5.3 Verify that HTTP requests to sensitive functionality use appropriate HTTP methods such as POST, PUT, PATCH, or DELETE, and not methods defined by the HTTP specification as “safe” such as HEAD, OPTIONS, or GET. Alternatively, strict validation of the Sec-Fetch-* request header fields can be used to ensure that the request did not originate from an inappropriate cross-origin call, a navigation request, or a resource load (such as an image source) where this is not expected.
Anything that changes data belongs behind POST, PUT, PATCH, or DELETE. A GET can be fired by an image tag on any site.
V4 API and Web Service (Part 5)
Two requirements, both about telling the client the truth: what a response contains, and how the connection is protected.
V4.1.1 Verify that every HTTP response with a message body contains a Content-Type header field that matches the actual content of the response, including the charset parameter to specify safe character encoding (e.g., UTF-8, ISO-8859-1) according to IANA Media Types, such as “text/”, “/+xml” and “/xml”.
Check your error responses and file downloads too. Those are the ones that quietly ship the wrong type.
V4.4.1 Verify that WebSocket over TLS (WSS) is used for all WebSocket connections.
Every ws:// URL becomes wss://, including the ones in configuration files that reach production.
V5 File Handling (Part 6)
Four requirements covering the two ways an upload hurts you: a file too big to process, and a file that is not what it claims to be.
V5.2.1 Verify that the application will only accept files of a size which it can process without causing a loss of performance or a denial of service attack.
Set a limit at the reverse proxy and again in the application, and check archives for their expanded size before unpacking.
V5.2.2 Verify that when the application accepts a file, either on its own or within an archive such as a zip file, it checks if the file extension matches an expected file extension and validates that the contents correspond to the type represented by the extension. This includes, but is not limited to, checking the initial ‘magic bytes’, performing image re-writing, and using specialized libraries for file content validation. For L1, this can focus just on files which are used to make specific business or security decisions. For L2 and up, this must apply to all files being accepted.
Read the first bytes of the file. An extension is a claim made by the uploader, not a fact.
V5.3.1 Verify that files uploaded or generated by untrusted input and stored in a public folder, are not executed as server-side program code when accessed directly with an HTTP request.
Serve the upload directory as static files only. If anything can execute there, an upload is a shell.
V5.3.2 Verify that when the application creates file paths for file operations, instead of user-submitted filenames, it uses internally generated or trusted data, or if user-submitted filenames or file metadata must be used, strict validation and sanitization must be applied. This is to protect against path traversal, local or remote file inclusion (LFI, RFI), and server-side request forgery (SSRF) attacks.
Store files under an identifier you generated. The original filename becomes a label in the database, never part of a path.
V6 Authentication (Parts 7 and 8)
Thirteen requirements, the largest chapter at Level 1. Most of the work is deleting password rules that never helped, not adding new ones.
V6.1.1 Verify that application documentation defines how controls such as rate limiting, anti-automation, and adaptive response, are used to defend against attacks such as credential stuffing and password brute force. The documentation must make clear how these controls are configured and prevent malicious account lockout.
Write down your rate limits and what happens when they trip, because V6.3.1 is verified against this document.
V6.2.1 Verify that user set passwords are at least 8 characters in length although a minimum of 15 characters is strongly recommended.
Set the minimum at 8 and prefer 15. Length is the one password rule that reliably helps.
V6.2.2 Verify that users can change their password.
A change-password form in account settings that works without the user contacting support.
V6.2.3 Verify that password change functionality requires the user’s current and new password.
Ask for the current password as well as the new one, so a borrowed session cannot lock the owner out.
V6.2.4 Verify that passwords submitted during account registration or password change are checked against an available set of, at least, the top 3000 passwords which match the application’s password policy, e.g. minimum length.
Load a list of the most common passwords and reject matches at registration, before hashing.
V6.2.5 Verify that passwords of any composition can be used, without rules limiting the type of characters permitted. There must be no requirement for a minimum number of upper or lower case characters, numbers, or special characters.
Delete the rules demanding a capital letter and a symbol. They push people towards Password1!.
V6.2.6 Verify that password input fields use type=password to mask the entry. Applications may allow the user to temporarily view the entire masked password, or the last typed character of the password.
The field is type=password, with an optional button to reveal it. Nothing more is asked for.
V6.2.7 Verify that “paste” functionality, browser password helpers, and external password managers are permitted.
Remove any handler that blocks paste, and any autocomplete="off" that stops a password manager filling the field.
V6.2.8 Verify that the application verifies the user’s password exactly as received from the user, without any modifications such as truncation or case transformation.
No trimming, no lowercasing, no cutting at 20 characters before hashing. Hash exactly what arrived.
V6.3.1 Verify that controls to prevent attacks such as credential stuffing and password brute force are implemented according to the application’s security documentation.
The code has to do what the document in V6.1.1 promised, without letting an attacker lock a real user out.
V6.3.2 Verify that default user accounts (e.g., “root”, “admin”, or “sa”) are not present in the application or are disabled.
Search the user table for admin, root, and test. Seed data from development is the usual way these arrive.
V6.4.1 Verify that system generated initial passwords or activation codes are securely randomly generated, follow the existing password policy, and expire after a short period of time or after they are initially used. These initial secrets must not be permitted to become the long term password.
Invite codes and temporary passwords come from a secure random generator, expire quickly, and work exactly once.
V6.4.2 Verify that password hints or knowledge-based authentication (so-called “secret questions”) are not present.
Delete the secret question feature. The answers are often public, and a user cannot change their mother’s maiden name.
V7 Session Management (Part 9)
Six requirements about the token that represents a logged-in user: how it is generated, and what has to happen when the session ends.
V7.2.1 Verify that the application performs all session token verification using a trusted, backend service.
The server decides whether a session is valid. A claim held by the browser is not a decision.
V7.2.2 Verify that the application uses either self-contained or reference tokens that are dynamically generated for session management, i.e. not using static API secrets and keys.
One shared API key for everybody is not a session. Issue a fresh token per login.
V7.2.3 Verify that if reference tokens are used to represent user sessions, they are unique and generated using a cryptographically secure pseudo-random number generator (CSPRNG) and possess at least 128 bits of entropy.
Sixteen random bytes from a cryptographic generator, not a counter and not Math.random.
V7.2.4 Verify that the application generates a new session token on user authentication, including re-authentication, and terminates the current session token.
Issue a new token at login and kill the old one, so a token captured beforehand is worthless.
V7.4.1 Verify that when session termination is triggered (such as logout or expiration), the application disallows any further use of the session. For reference tokens or stateful sessions, this means invalidating the session data at the application backend. Applications using self-contained tokens will need a solution such as maintaining a list of terminated tokens, disallowing tokens produced before a per-user date and time or rotating a per-user signing key.
Logout has to change something on the server. Deleting the cookie only hides the token from the honest user.
V7.4.2 Verify that the application terminates all active sessions when a user account is disabled or deleted (such as an employee leaving the company).
Disabling an account has to reach the sessions that account already has open.
V8 Authorization (Part 10)
Four requirements, and the hardest chapter to automate, because only you know who is supposed to see what.
V8.1.1 Verify that authorization documentation defines rules for restricting function-level and data-specific access based on consumer permissions and resource attributes.
Write down who may call what, and who may see which records. Authorization bugs are usually undocumented assumptions.
V8.2.1 Verify that the application ensures that function-level access is restricted to consumers with explicit permissions.
Every route states the permission it needs. A route with no check is a route open to everyone.
V8.2.2 Verify that the application ensures that data-specific access is restricted to consumers with explicit permissions to specific data items to mitigate insecure direct object reference (IDOR) and broken object level authorization (BOLA).
Scope the query to the owner. WHERE id = $1 is the bug, WHERE id = $1 AND user_id = $2 is the fix.
V8.3.1 Verify that the application enforces authorization rules at a trusted service layer and doesn’t rely on controls that an untrusted consumer could manipulate, such as client-side JavaScript.
Hiding a button is not authorization. The check belongs in the handler that button calls.
V9 Self-contained Tokens (Part 11)
Four requirements about tokens that carry their own claims, which in practice means JWTs. All four come down to not letting a token describe itself.
V9.1.1 Verify that self-contained tokens are validated using their digital signature or MAC to protect against tampering before accepting the token’s contents.
Verify the signature first, then read the claims. Decoding a token proves nothing about who wrote it.
V9.1.2 Verify that only algorithms on an allowlist can be used to create and verify self-contained tokens, for a given context. The allowlist must include the permitted algorithms, ideally only either symmetric or asymmetric algorithms, and must not include the ‘None’ algorithm. If both symmetric and asymmetric must be supported, additional controls will be needed to prevent key confusion.
Name the algorithms you accept. Trusting the algorithm named in the token includes trusting none.
V9.1.3 Verify that key material that is used to validate self-contained tokens is from trusted pre-configured sources for the token issuer, preventing attackers from specifying untrusted sources and keys. For JWTs and other JWS structures, headers such as ‘jku’, ‘x5u’, and ‘jwk’ must be validated against an allowlist of trusted sources.
Keys come from your own configuration. A token that points at its own key is a token that signs itself.
V9.2.1 Verify that, if a validity time span is present in the token data, the token and its content are accepted only if the verification time is within this validity time span. For example, for JWTs, the claims ‘nbf’ and ‘exp’ must be verified.
Check exp and nbf on every request, allowing a few seconds of clock difference and no more.
V10 OAuth and OIDC (Part 12)
Five requirements that all land on the authorization server rather than your application. With a hosted identity provider, this chapter is configuration.
V10.4.1 Verify that the authorization server validates redirect URIs based on a client-specific allowlist of pre-registered URIs using exact string comparison.
Compare the redirect URI as one whole string. Prefix and wildcard matching is how tokens end up on someone else’s domain.
V10.4.2 Verify that, if the authorization server returns the authorization code in the authorization response, it can be used only once for a token request. For the second valid request with an authorization code that has already been used to issue an access token, the authorization server must reject a token request and revoke any issued tokens related to the authorization code.
A code redeemed twice means it leaked. Refuse the second attempt and revoke whatever the first one issued.
V10.4.3 Verify that the authorization code is short-lived. The maximum lifetime can be up to 10 minutes for L1 and L2 applications and up to 1 minute for L3 applications.
Ten minutes at most, and shorter is better. The code only has to survive a single redirect.
V10.4.4 Verify that for a given client, the authorization server only allows the usage of grants that this client needs to use. Note that the grants ‘token’ (Implicit flow) and ‘password’ (Resource Owner Password Credentials flow) must no longer be used.
Enable one grant per client. Implicit and password grants should be switched off everywhere.
V10.4.5 Verify that the authorization server mitigates refresh token replay attacks for public clients, preferably using sender-constrained refresh tokens, i.e., Demonstrating Proof of Possession (DPoP) or Certificate-Bound Access Tokens using mutual TLS (mTLS). For L1 and L2 applications, refresh token rotation may be used. If refresh token rotation is used, the authorization server must invalidate the refresh token after usage, and revoke all refresh tokens for that authorization if an already used and invalidated refresh token is provided.
Rotate the refresh token on every use, and revoke the whole chain when an already-used one comes back.
V11 Cryptography (Part 13)
Three requirements, none of which ask you to design anything. They ask you to stop using algorithms that are already broken.
V11.3.1 Verify that insecure block modes (e.g., ECB) and weak padding schemes (e.g., PKCS#1 v1.5) are not used.
Search for ECB mode and PKCS#1 v1.5 padding. Both leak information about the data they were meant to protect.
V11.3.2 Verify that only approved ciphers and modes such as AES with GCM are used.
AES-256-GCM, with a fresh nonce for every message, from a library rather than assembled by hand.
V11.4.1 Verify that only approved hash functions are used for general cryptographic use cases, including digital signatures, HMAC, KDF, and random bit generation. Disallowed hash functions, such as MD5, must not be used for any cryptographic purpose.
SHA-256 or better. MD5 and SHA-1 may stay only where nothing about security depends on them, such as cache keys.
V12 Secure Communication (Part 14)
Three requirements about the transport. This is reverse proxy and certificate work rather than application code.
V12.1.1 Verify that only the latest recommended versions of the TLS protocol are enabled, such as TLS 1.2 and TLS 1.3. The latest version of the TLS protocol must be the preferred option.
Enable TLS 1.2 and 1.3, disable everything older, and put 1.3 first in the preference order.
V12.2.1 Verify that TLS is used for all connectivity between a client and external facing, HTTP-based services, and does not fall back to insecure or unencrypted communications.
Port 80 redirects to 443 and serves nothing else. Calls your server makes to other services count too.
V12.2.2 Verify that external facing services use publicly trusted TLS certificates.
A certificate from a public authority on anything the internet can reach. Self-signed belongs on internal hosts only.
V13 Configuration (Part 15)
One requirement, and one of the easiest to check: a deployment must not carry its own version control history.
V13.4.1 Verify that the application is deployed either without any source control metadata, including the .git or .svn folders, or in a way that these folders are inaccessible both externally and to the application itself.
Request /.git/config from production. If anything comes back, your source code is public.
V14 Data Protection (Part 16)
Two requirements about where sensitive values are allowed to sit, in transit and in the browser.
V14.2.1 Verify that sensitive data is only sent to the server in the HTTP message body or header fields, and that the URL and query string do not contain sensitive information, such as an API key or session token.
Tokens go in a header or the request body. A token in a query string ends up in access logs and Referer headers.
V14.3.1 Verify that authenticated data is cleared from client storage, such as the browser DOM, after the client or session is terminated. The ‘Clear-Site-Data’ HTTP response header field may be able to help with this but the client-side should also be able to clear up if the server connection is not available when the session is terminated.
On logout, clear what you stored in the browser, and make the browser able to do it alone if the server is unreachable.
V15 Secure Coding and Architecture (Part 17)
Three requirements about the code you did not write, plus one about not over-sharing in your responses.
V15.1.1 Verify that application documentation defines risk based remediation time frames for 3rd party component versions with vulnerabilities and for updating libraries in general, to minimize the risk from these components.
State how fast you patch, by severity. Without a number, “we update regularly” cannot be verified by anyone.
V15.2.1 Verify that the application only contains components which have not breached the documented update and remediation time frames.
Run a dependency scanner in CI and fail the build when a component is older than your own deadline.
V15.3.1 Verify that the application only returns the required subset of fields from a data object. For example, it should not return an entire data object, as some individual fields should not be accessible to users.
Build a response object holding the fields the caller needs. Serializing the database row ships the password hash with it.
Common Mistakes and Troubleshooting
Reusing ASVS 4.0 requirement IDs. Version 5.0 renumbered and reorganised everything. In 4.0, V2 was Authentication. In 5.0, V2 is Validation and Business Logic, and Authentication moved to V6. Old tickets, spreadsheets, and blog posts that cite bare IDs are not just outdated, they point at the wrong subject entirely. Always write the version alongside the ID.
Reading only the summary line. The explanations on this page are a way in, not the standard. Several requirements carry a second sentence that changes the amount of work substantially, V7.4.1 and V10.4.5 in particular. When you are about to mark something as passed, read OWASP’s sentence, not mine.
Treating “not applicable” as a free pass. Every skipped requirement needs a written reason and a date. Silently omitting one is indistinguishable from failing it, and it is the first thing a reviewer looks for.
Scoping too broadly. “All of our systems” is not a scope. One application, its API, and the data store behind it is a scope. Five services means five verifications, and they can legitimately reach five different conclusions.
Assuming Level 1 means secure. It means you closed the 70 most broadly applicable requirements. That is a real milestone, and it is also the point where Level 2 becomes the next target rather than the end of the road.
Running a scanner and calling it verification. Automated tools cover maybe a third of Level 1, and none of the documentation requirements. No scanner can tell you whether V8.2.2 passes, because only you know which user is supposed to see which record.
Best Practices
Keep the record in the application repository. A checklist in a wiki or a spreadsheet goes stale within a month. In the repo, one pull request can change the code and tick the box in the same diff, which is exactly the evidence trail you will want later.
Record evidence, not just status. Under each requirement you mark as passed, write how you verified it: the command you ran, the test that covers it, or the file and line where the control lives. Six months on, “passed” tells you nothing, while “covered by test/auth_test.go:142” tells you whether it is still true.
Automate the part that genuinely automates. A decent slice of Level 1 is checkable by a CI job: security headers, cookie attributes, TLS versions, Content-Type correctness, an exposed .git folder. Push those into your pipeline so a regression fails the build instead of surviving until the next audit. The dependency requirements in V15 fit the same pattern, and Scan Container Images for Vulnerabilities with Trivy on Ubuntu covers a scanner that handles much of it.
Re-verify on a schedule. A passed requirement describes your application on the day you checked it. Code changes. Pick an interval, quarterly is common, and re-run the list instead of trusting last year’s answer.
Record the exact standard version. “ASVS 5.0.0, git tag v5.0.0” costs one line in your notes and makes your result reproducible by somebody else.
Do not let the list replace thinking. ASVS is a floor built from the things that go wrong across most applications. It knows nothing about your business logic. The requirements that matter most in practice, authorization in V8 above all, need somebody who understands what your application is actually for.
Conclusion
You now know what ASVS is, how its three levels relate, and exactly what the 70 Level 1 requirements say. You also know which of them are code, which are configuration, and which are documents that have to exist before other requirements can be verified against them.
The next part starts on the code, with V1 Encoding and Sanitization and its eight Level 1 requirements: parameterized database queries, contextual output encoding, safe URL construction, OS command handling, HTML sanitization, and XML parser configuration. Every control is shown broken first and then fixed, with the two versions one line apart, in Node.js with Fastify and in Go with Fiber. If you want an application to practise on, Build a REST API with Fastify and MySQL on Ubuntu and Build a REST API in Go with Fiber v3 on Ubuntu each produce one in about an hour.
The OWASP Application Security Verification Standard is licensed under Creative Commons Attribution-ShareAlike 4.0, which is what permits the requirement text to be reproduced here.