The OWASP Top 10 is a list of the most important security risks in web applications, built from data on real vulnerabilities and a survey of security practitioners. Clients, auditors, and penetration testers often use it as a checklist, so development teams are regularly asked whether their application is "OWASP compliant". The list is better used as a map of where applications usually go wrong. This guide goes through the 2025 edition with examples of how each risk shows up in the business applications we build and review.
The 2025 List at a Glance
| Category | What it looks like | Main prevention |
|---|---|---|
| A01 Broken Access Control | Changing an ID in the URL shows another customer's invoice, or a normal user can call an admin endpoint | Check ownership and role on the server for every request, deny by default |
| A02 Security Misconfiguration | Debug mode on in production, default passwords, an open storage bucket, or a database port open to the internet | Hardened configuration kept in code and reviewed, with separate settings per environment |
| A03 Software Supply Chain Failures | An outdated library with a known vulnerability, or a compromised package pulled into the build | Lock files, dependency scanning, and a routine for updates |
| A04 Cryptographic Failures | Passwords stored with MD5, sensitive data sent over HTTP, or encryption keys committed to Git | Framework password hashing, HTTPS everywhere, keys in a secrets manager |
| A05 Injection | SQL built by joining strings with user input, or user text rendered as HTML | Parameterized queries and the ORM, output escaping by the template engine |
| A06 Insecure Design | A voucher that can be redeemed many times, or an OTP with no attempt limit | Threat modelling of business flows and limits designed in from the start |
| A07 Authentication Failures | No limit on login attempts, weak password reset, or sessions that never expire | Rate limits, multi-factor authentication for admins, secure session handling |
| A08 Software or Data Integrity Failures | Accepting a payment webhook without checking its signature, or deploying unsigned builds | Verify signatures, protect the CI/CD pipeline, avoid unsafe deserialization |
| A09 Security Logging and Alerting Failures | A breach found months later because failed logins and permission errors were never logged | Log security events centrally and alert on unusual patterns |
| A10 Mishandling of Exceptional Conditions | An error that leaks a stack trace, or a failure that leaves a transaction half done and the user with access | Fail closed, use transactions, and return generic error messages |
Compared with the 2021 edition, server-side request forgery (SSRF) is now part of broken access control, supply chain risks have their own category, and error handling is new at A10. The first category has stayed at the top for years, and it is also the one automated scanners find least reliably.
Broken Access Control Deserves Most of Your Time
Frameworks protect against injection and cross-site scripting by default when you use them as intended. Access control is different, because only your code knows that invoice 1042 belongs to company A and that a branch manager may approve refunds up to a certain amount. Every endpoint that reads or changes data needs a check on the server, and hiding a button in the interface is not that check. In Laravel this means Policies, in Django object-level permissions in Django REST Framework, and in Node.js a shared authorization layer that every route goes through.
- Load records through the current user or tenant, for example $request->user()->orders()->findOrFail($id), so another tenant's ID simply returns 404.
- Write automated tests that log in as user A and request user B's data for every important endpoint.
- Check file downloads and exports too. They are often added later and skip the normal checks.
- Block requests from the server to internal addresses and cloud metadata endpoints when the application fetches URLs supplied by users.
Configuration and Dependencies
Misconfiguration and outdated components cause many of the incidents we see on servers that were never attacked in any clever way. Keep production settings in code or in a reviewed template, turn off debug output, remove default accounts, and make storage buckets private by default. Run composer audit, npm audit, or pip-audit in CI, enable Dependabot or Renovate, and schedule a regular slot for updates so they do not pile up until a major upgrade becomes frightening.
Design Flaws That No Scanner Finds
Insecure design is about business rules. A promotion that can be applied twice by sending two requests at the same time, a transfer that does not check the balance inside the same database transaction, or an OTP that can be guessed because there is no attempt limit. These need a short threat modelling session when a feature is designed: ask how a dishonest user would abuse this flow, then add limits, locks, and checks before the code is written.
Building Security Into the Process
- 1Add a short security checklist based on the Top 10 to the pull request template and to the definition of done.
- 2Run static analysis such as SonarQube or Semgrep and dependency scanning on every pull request.
- 3Write access control tests for every endpoint that touches customer or financial data.
- 4Scan the running application with a dynamic scanner such as OWASP ZAP or Qualys before major releases.
- 5Log logins, permission failures, and admin actions to a central place and set alerts on unusual spikes.
- 6Have an external penetration test at least once a year or before launching a system that handles payments or personal data.
For applications that process personal data of Indonesian users, these practices also support the security obligations in the Personal Data Protection Law (UU PDP). A record of scans, fixes, and tests is useful evidence when a client or auditor asks how the application is protected.
Key takeaways
- Use the OWASP Top 10 as a map of where applications usually fail.
- Spend the most effort on access control and test it with real users and real IDs.
- Keep configuration reviewed in code and update dependencies on a schedule.
- Look for business logic abuse during design, because scanners cannot find it.
- Automate static, dependency, and dynamic scans, and log security events centrally.


