Secure Code/Explainer
OWASP Top 10 explained: the ten web risks and how to defend against them
The 2025 edition of the OWASP Top 10 reorders the classic web risks and adds a new one about error handling. What each category means, how it shows up in real code and what stops it.
sha256("/secure-code/owasp-top-10/")The OWASP Top 10 is a ranked list of the most critical security risks to web applications, published by the OWASP Foundation, a non-profit organisation. It is not a standard you can pass or fail. It is an awareness document built from real test data, and it gives developers, testers and managers a shared vocabulary for the problems that keep turning up in code reviews and penetration tests.
The current edition is the OWASP Top 10:2025. It is based on data about more than 2.8 million applications donated by organisations, plus a community survey that covers risks the test data does not yet show well. This article walks through all ten categories in order, with a short example and the defence for each.
What changed since 2021
Most of the list will look familiar. Broken access control stays at the top. Security misconfiguration climbs from fifth to second place, which reflects how much behaviour now lives in configuration: cloud settings, framework flags, headers. Cryptographic failures drop from second to fourth, injection from third to fifth and insecure design from fourth to sixth.
- New: A10 Mishandling of Exceptional Conditions covers poor error handling, logic errors and systems that .
- Broadened: A03 Software Supply Chain Failures replaces and widens the 2021 category Vulnerable and Outdated Components.
- Merged: server-side request forgery (), a separate entry in 2021, is now part of A01 Broken Access Control.
- Renamed: A07 is now Authentication Failures, and A09 is Security Logging and Alerting Failures, with the emphasis on alerts that someone acts on.
The ten categories at a glance
| Rank | Category | Core defence |
|---|---|---|
| A01 | Broken Access Control | Deny by default, check permissions on the server for every request |
| A02 | Security Misconfiguration | Hardened, repeatable configuration, no defaults or debug modes in production |
| A03 | Software Supply Chain Failures | Lockfiles, reviewed updates, an SBOM and verified artefacts |
| A04 | Cryptographic Failures | TLS everywhere, vetted libraries, proper password hashing |
| A05 | Injection | Parameterised queries and context aware output encoding |
| A06 | Insecure Design | Threat modelling and abuse cases before code |
| A07 | Authentication Failures | Multi-factor authentication, rate limits, safe session handling |
| A08 | Software or Data Integrity Failures | Signature and integrity checks, no unsafe deserialisation |
| A09 | Security Logging and Alerting Failures | Log security events and alert a person when they matter |
| A10 | Mishandling of Exceptional Conditions | Fail closed, handle errors centrally, hide internals |
Order and names as published by OWASP for the 2025 edition.
A01 to A03: access, configuration and dependencies
A01 Broken Access Control means a user can see or do something they should not. The classic example is an invoice URL with a numeric ID: change the number and you see another customer’s invoice, because the server only checked that you were logged in, not that the invoice is yours. Deny by default and check ownership or role on the server for every request, ideally in one shared place rather than in each controller. Because SSRF now lives here, also restrict which hosts your server may contact when a user supplies a URL.
$stmt = $pdo->prepare('SELECT id, total FROM invoices WHERE id = :id AND customer_id = :cid');
$stmt->execute(['id' => $invoiceId, 'cid' => $currentUser->id]);
$invoice = $stmt->fetch(PDO::FETCH_ASSOC);
if ($invoice === false) {
http_response_code(404); // same answer for "not found" and "not yours"
exit;
}A02 Security Misconfiguration covers debug modes left on, default accounts, directory listings, verbose error pages and missing security headers. A framework that shows a full stack trace to every visitor is a typical case. Keep configuration in version control, build each environment from the same hardened template and review the differences between staging and production.
A03 Software Supply Chain Failures is about everything you did not write yourself: libraries, build tools, container images and the pipeline that assembles them. An abandoned package with a known vulnerability counts, and so does a dependency that a stranger has quietly taken over. Commit lockfiles, review updates before you merge them, keep an inventory of components (an ) and prefer releases that are signed.
A04 to A06: cryptography, injection and design
A04 Cryptographic Failures covers data sent or stored without encryption, outdated algorithms, hard-coded keys and passwords stored with fast hashes. Storing passwords as plain SHA-256 is a common example. Use TLS for every connection, rely on vetted libraries instead of your own constructions, keep keys out of the code and hash passwords with a dedicated algorithm such as Argon2id.
A05 Injection happens when untrusted input is mixed into a command or query and changes its meaning. SQL injection, cross-site scripting (XSS) and operating system command injection all belong here. The defence is structural: parameterised queries for databases, context aware output encoding for HTML and argument lists instead of shell strings. A Content Security Policy adds a second layer against XSS.
A06 Insecure Design is a flaw in the plan, not a bug in the code. A password reset that relies on guessable security questions, or a checkout that trusts a price sent by the browser, stays insecure however carefully it is implemented. , written abuse cases and proven design patterns before the first line of code are the defence.
A07 to A10: logins, integrity, logging and errors
A07 Authentication Failures include weak password rules, no protection against (automated login attempts with passwords leaked from other services), sessions that never expire and missing multi-factor authentication. Offer MFA, rate limit login attempts, check new passwords against lists of known breached passwords and issue a fresh session ID after login.
A08 Software or Data Integrity Failures means trusting code or data without checking that it is what it claims to be. Update mechanisms without signature checks, pipelines that pull unverified plugins and unsafe deserialisation of objects a user can control all fall here. Verify signatures and checksums, and accept only simple data formats such as JSON from untrusted sources.
A09 Security Logging and Alerting Failures is one reason breaches are often discovered long after they began. If failed logins, permission errors and changes to admin accounts are not logged, or nobody is alerted when they spike, an attack runs quietly. Log security events in a structured format, keep secrets and personal data out of the logs and send a small number of meaningful alerts to a person.
A10 Mishandling of Exceptional Conditions covers what happens when things go wrong: a timeout, a full disk, an unexpected empty value. A permission check that throws an exception and then lets the request through is a fail open bug. Handle errors in one central place, fail closed, roll back partial transactions and show users a generic message while the details go to the log.
async function canEdit(user, doc) {
try {
return await policy.check(user, "edit", doc);
} catch (err) {
logger.error({ err, userId: user.id }, "policy check failed");
return false;
}
}Putting the list to work
- For every endpoint, write down which role or owner may use it, and test with a second account that it cannot reach the first account’s data.
- Add dependency and secret scanning to your pipeline so supply chain problems surface on every merge request.
- Set a Content Security Policy and the other security headers, and check them after each deployment.
- Review error handling paths with the same care as the normal path, and test what happens when a dependency times out.
- Decide which security events trigger an alert and who receives it, then test that the alert arrives.
Most of the list is not exotic. It is the ordinary code path that nobody tested with a second account.
Sources and further reading
- OWASP Top 10:2025top10.owasp.org
- OWASP Top 10:2025, introduction and changestop10.owasp.org
- OWASP SQL Injection Prevention Cheat Sheetcheatsheetseries.owasp.org
- OWASP Cross Site Scripting Prevention Cheat Sheetcheatsheetseries.owasp.org
General information, checked on 31 October 2026. Test changes on one system first and keep a way back. Found something outdated? Tell the editors.