Security is not a feature you add before launch. It is a property of how you design, build, and operate software from day one. The OWASP Top 10 provides a consistently updated catalogue of the most critical web application security risks, based on real-world breach data and vulnerability reports from organisations worldwide.
The 2025 edition of the OWASP Top 10 reflects the evolving threat landscape, including risks introduced by AI-generated code and increasingly sophisticated supply chain attacks. At Pepla, every developer is trained on these vulnerabilities, and our code review process includes explicit security checkpoints for each category.
This article walks through each of the Top 10 with practical, implementable defences. Not theory; code-level guidance you can apply today.
A01: Broken Access Control
Broken access control has held the number one position since the 2021 edition, and for good reason. It occurs when users can access resources or perform actions beyond their intended permissions. This includes accessing another user's data by modifying an ID in the URL, escalating from a regular user to an admin, or bypassing access checks by manipulating API requests.
Deny by default -- every endpoint and resource should require explicit permission before granting access.
Defences
- Deny by default. Every endpoint and resource should require explicit permission. If there is no access rule, the answer is "no."
- Server-side enforcement. Never rely on the client to enforce access control. Hiding a button in the UI is not security; checking permissions on the server is.
- Object-level authorisation. When a user requests
/api/orders/456, verify that user 123 actually owns order 456. This check must happen on every request, not just the first one. - Centralise access control logic. Scattering permission checks across hundreds of controllers leads to missed checks. Use middleware, policy frameworks, or decorators that enforce rules consistently.
- Log and alert on access control failures. A user repeatedly hitting 403 errors may be probing for vulnerabilities.
A02: Cryptographic Failures
Previously called "Sensitive Data Exposure," this category covers failures in protecting data through encryption. Storing passwords in plain text, using MD5 or SHA1 for hashing, transmitting sensitive data over HTTP, or using weak encryption keys all fall here.
Defences
- Encrypt data in transit. TLS everywhere, no exceptions. Enforce HTTPS with HSTS headers and redirect HTTP to HTTPS.
- Encrypt data at rest. Sensitive data in databases and file storage should be encrypted. Use the cloud provider's native encryption (Azure SSE, AWS KMS) as a baseline.
- Hash passwords properly. Use bcrypt, scrypt, or Argon2id with appropriate work factors. Never use MD5, SHA1, or SHA256 alone for password hashing.
- Manage keys securely. Store encryption keys in a dedicated key management service, not in your codebase, environment variables, or configuration files. Rotate keys regularly.
- Classify your data. Not all data needs the same protection level. Identify what is sensitive (PII, financial data, health records) and apply appropriate controls.
Broken access control is the number one web vulnerability -- enforce it at every layer.
A03: Injection
Injection attacks occur when untrusted data is sent to an interpreter as part of a command or query. SQL injection remains the most common form, but NoSQL injection, OS command injection, LDAP injection, and template injection all follow the same pattern.
Defences
- Use parameterised queries. This is the single most effective defence against SQL injection. ORMs handle this automatically; raw SQL queries must use parameter binding.
- Validate and sanitise input. Validate data types, lengths, and formats at the API boundary. Reject unexpected input rather than trying to clean it.
- Use allowlists over denylists. Define what is allowed rather than what is blocked. Denylist approaches inevitably miss edge cases.
- Apply least privilege to database accounts. Your application's database user should not have DROP TABLE permissions. Separate read and write database accounts with minimal required privileges.
SQL injection has been a known vulnerability for over 25 years, yet it still appears in production applications regularly. The fix is straightforward: use parameterised queries. There is no excuse for SQL injection in 2026.
A04: Insecure Design
This category, added in the 2021 edition, addresses design-level flaws that cannot be fixed by perfect implementation. A password reset flow that sends the new password via email is insecurely designed regardless of how well the code is written.
Defences
- Threat modelling. Before writing code, identify what could go wrong. Use STRIDE (Spoofing, Tampering, Repudiation, Information Disclosure, Denial of Service, Elevation of Privilege) to systematically analyse each component.
- Security requirements in user stories. "As a user, I can reset my password" should include "via a time-limited, single-use token sent to my verified email address."
- Rate limiting on sensitive operations. Login attempts, password resets, OTP verification, and account creation should all have rate limits to prevent abuse.
A05: Security Misconfiguration
Default credentials, unnecessary services, overly permissive CORS policies, verbose error messages in production, and missing security headers all fall under this category. It is the broadest category and often the easiest to exploit.
Defences
- Harden all environments. Remove default accounts, disable unnecessary features, and configure security headers (Content-Security-Policy, X-Content-Type-Options, Strict-Transport-Security).
- Automate configuration checks. Use tools like ScoutSuite, Prowler (AWS), or Azure Advisor to continuously audit your cloud configuration.
- Separate debug from production. Stack traces, detailed error messages, and debugging endpoints must never be exposed in production.
- Apply the principle of least privilege everywhere. Cloud IAM roles, network security groups, API permissions: start with nothing and add only what is needed.
Parameterised queries eliminate injection attacks. There is no excuse for concatenating user input.
A06: Vulnerable and Outdated Components
Every dependency is an attack surface. The Log4Shell vulnerability in 2021 demonstrated how a single vulnerable library can compromise thousands of organisations. In 2026, supply chain attacks have become more sophisticated, including compromised npm packages, typosquatting, and malicious maintainer takeovers.
Defences
- Automated dependency scanning. Integrate Dependabot, Snyk, or Renovate into your CI pipeline. Block deployments when critical vulnerabilities are detected.
- Maintain a software bill of materials (SBOM). Know exactly what is in your dependency tree. CycloneDX and SPDX are standard SBOM formats.
- Update regularly. Do not let dependencies age for months. Schedule weekly or bi-weekly dependency update cycles.
- Evaluate dependencies before adoption. Check maintenance activity, download counts, known vulnerabilities, and the number of transitive dependencies before adding a new package.
A07: Identification and Authentication Failures
Weak authentication mechanisms, session management flaws, and credential stuffing vulnerabilities. This includes allowing weak passwords, not implementing multi-factor authentication, and failing to invalidate sessions properly.
Defences
- Implement MFA. Multi-factor authentication should be available for all users and required for privileged accounts. TOTP and WebAuthn/passkeys are the recommended methods in 2026.
- Enforce strong password policies. Minimum length (12+ characters), check against breached password databases (Have I Been Pwned API), and do not impose arbitrary complexity rules that lead to predictable patterns.
- Secure session management. Generate cryptographically random session IDs, set appropriate expiration, and invalidate sessions on logout and password change.
- Rate limit authentication endpoints. Lock accounts temporarily after repeated failures. Implement progressive delays or CAPTCHA challenges.
A08: Software and Data Integrity Failures
This covers assumptions about software updates, critical data, and CI/CD pipelines without verifying integrity. Using dependencies from untrusted sources, deserialising untrusted data, and CI/CD pipelines without integrity verification all qualify.
Defences
- Verify package integrity. Use lockfiles with integrity hashes. Verify package signatures where available.
- Secure your CI/CD pipeline. Treat your pipeline configuration as security-critical code. Require reviews for pipeline changes, limit access to deployment credentials, and log all pipeline executions.
- Never deserialise untrusted data. If you must accept serialised objects, use safe deserialisation libraries with type allowlists.
A09: Security Logging and Monitoring Failures
If you cannot detect an attack, you cannot respond to it. Insufficient logging and monitoring means breaches go undetected for weeks or months. The average time to detect a breach is still measured in hundreds of days.
Defences
- Log all security-relevant events. Login attempts (successful and failed), access control failures, input validation failures, and privilege changes should all generate log entries.
- Centralise logs. Ship logs to a centralised platform (ELK Stack, Datadog, Azure Sentinel) where they can be correlated, searched, and alerted on.
- Set up alerts for anomalies. Unusual login patterns, spike in 403 errors, access from unexpected geographies: these should trigger automated alerts.
- Protect log integrity. Logs should be append-only and tamper-evident. An attacker who gains access should not be able to delete their tracks.
The best security controls are useless if nobody notices when they are triggered. Invest as much in detection and response as you do in prevention. You cannot prevent every attack, but you can detect and respond to them quickly.
A10: Server-Side Request Forgery (SSRF)
SSRF occurs when an application fetches a remote resource based on user-supplied input without validating the destination. An attacker can make the server request internal resources (metadata APIs, internal services, localhost endpoints) that should not be externally accessible.
Defences
- Validate and sanitise URLs. If your application fetches URLs provided by users, validate the scheme (only allow http/https), resolve the hostname, and reject requests to private IP ranges (10.x.x.x, 172.16-31.x.x, 192.168.x.x, 169.254.169.254).
- Use allowlists. If the application only needs to fetch from specific domains, maintain an explicit allowlist.
- Network segmentation. Place your application servers in a network segment that cannot reach internal infrastructure directly. Use dedicated service accounts for internal API access.
- Disable unnecessary URL schemes. Block file://, gopher://, dict://, and other schemes that the application does not need.
Security vulnerabilities are business risks -- investing in security-first development is insurance, not cost.
Building a Security Culture
Tools and checklists are necessary but not sufficient. Security-first development requires a culture where every developer considers security implications in every design decision and every code review.
At Pepla, we embed security into our development process through regular security training for all developers, explicit security criteria in our definition of done, automated security scanning in every pipeline, and security-focused code review checklists. The OWASP Top 10 provides the framework; your engineering culture provides the execution.
Security is not an afterthought in Pepla's development process. Every code review includes security checks, and our hosting environments are hardened against the OWASP Top 10 by default.
Security vulnerabilities are not just technical problems. They are business risks that can result in data breaches, regulatory penalties, reputational damage, and loss of customer trust. Investing in security-first development is not a cost; it is insurance against outcomes that are far more expensive.




