Skip to content
SDETRole
SDETRole

Lesson 4 of 9 · Advanced Quality Engineering

OWASP Top 10 for Testers

The OWASP Top 10:2025 from a tester's perspective: what each risk means, how to spot it during functional testing, checks you can automate, and where to stop.

intermediate 15 min read Updated

You will learn to

  • Name and explain the ten categories in the OWASP Top 10:2025
  • Describe what changed from the 2021 edition, including the two new categories
  • Design practical test ideas for each category during normal functional testing
  • Automate simple access control, security header and error handling checks
  • Work within ethical and legal limits and know when to involve security specialists

Prerequisites

  • Security Testing Basics
  • HTTP Fundamentals
  • REST APIs

The OWASP Top 10 is an awareness document from the Open Worldwide Application Security Project that ranks the most critical categories of web application security risk. It is not a complete security standard or a checklist that proves an application is secure; it is a shared vocabulary and a sensible place to focus. For testers it is a map: each category suggests questions to ask and checks to add while you are already testing features.

This lesson uses the OWASP Top 10:2025, published at top10.owasp.org. OWASP revises the list every few years, and categories move, merge and get renamed between editions. The underlying weaknesses stay remarkably stable, so if you learned the 2021 list, most of your test ideas still apply; they just sit under different IDs.

The 2025 list at a glance

ID Category One-line meaning
A01:2025 Broken Access Control Users can act outside their intended permissions; now also covers SSRF
A02:2025 Security Misconfiguration Unsafe defaults, unnecessary features enabled, missing hardening
A03:2025 Software Supply Chain Failures Compromised or vulnerable dependencies, build tools and pipelines
A04:2025 Cryptographic Failures Sensitive data exposed through missing or weak cryptography
A05:2025 Injection Untrusted input interpreted as code or commands (includes XSS)
A06:2025 Insecure Design Flaws in the design itself, not just the implementation
A07:2025 Authentication Failures Weak login, session and credential handling
A08:2025 Software or Data Integrity Failures Trusting code, updates or data without verifying integrity
A09:2025 Security Logging and Alerting Failures Attacks are not recorded, detected or acted on
A10:2025 Mishandling of Exceptional Conditions Errors and unusual situations are not prevented, detected or handled safely

What changed from 2021

2021 2025 Change
A01 Broken Access Control A01 Broken Access Control Still first; now also covers Server-Side Request Forgery
A05 Security Misconfiguration A02 Security Misconfiguration Moved up
A06 Vulnerable and Outdated Components A03 Software Supply Chain Failures New, broader category: the whole build and distribution chain, not just outdated libraries
A02 Cryptographic Failures A04 Cryptographic Failures Moved down
A03 Injection A05 Injection Moved down
A04 Insecure Design A06 Insecure Design Moved down
A07 Identification and Authentication Failures A07 Authentication Failures Renamed
A08 Software and Data Integrity Failures A08 Software or Data Integrity Failures Renamed slightly
A09 Security Logging and Monitoring Failures A09 Security Logging and Alerting Failures Renamed, with the emphasis on alerting
A10 Server-Side Request Forgery (SSRF) merged into A01 No longer a separate category
none A10 Mishandling of Exceptional Conditions New

A01: Broken Access Control

Top of the list, and the category where functional testers find the most real issues, because it is about who can do what, which you already test.

Test ideas:

  • Horizontal access (IDOR): as user A, request user B’s resources by changing an ID in the URL or body: /orders/1001 to /orders/1002.
  • Vertical access: as a normal user, call admin endpoints directly, even if the UI hides the button.
  • Method tampering: a GET may be protected while PUT or DELETE on the same resource is not.
  • Unauthenticated access: call endpoints with no token, an expired token and a token for a deleted user.
  • Forced browsing: request admin or authenticated pages by URL, without navigating through the UI.
  • Client-side-only checks: anything the UI blocks with JavaScript but the API accepts when called directly with curl or an API client.
  • Permissive CORS: untrusted origins allowed to call the API with credentials.

These checks automate well at the API level:

import { test, expect } from '@playwright/test';

test.describe('order access control', () => {
  test('a customer cannot read another customer\'s order', async ({ playwright }) => {
    const alice = await playwright.request.newContext({
      baseURL: process.env.API_URL,
      extraHTTPHeaders: { Authorization: `Bearer ${process.env.ALICE_TOKEN}` },
    });
    // Order 5002 belongs to Bob in the seeded test data.
    const res = await alice.get('/orders/5002');
    expect([403, 404]).toContain(res.status());
    await alice.dispose();
  });

  test('admin endpoint rejects a customer token', async ({ playwright }) => {
    const alice = await playwright.request.newContext({
      baseURL: process.env.API_URL,
      extraHTTPHeaders: { Authorization: `Bearer ${process.env.ALICE_TOKEN}` },
    });
    const res = await alice.delete('/admin/users/42');
    expect(res.status()).toBe(403);
    await alice.dispose();
  });
});

Accepting either 403 or 404 for the first test is deliberate: returning 404 avoids revealing that the order exists. Agree the expected behaviour with the team, then assert it exactly.

Server-Side Request Forgery (SSRF)

SSRF was its own category (A10) in 2021 and is now part of A01. It fits: the server is tricked into using its own network position and permissions to reach things the user should never be able to reach. Any feature where the server fetches a URL the user provides, such as “import from URL”, webhooks, link previews and PDF renderers, can be abused to make the server request internal addresses. Test that such features reject internal and cloud metadata addresses and non-HTTP schemes, following an approach agreed with your security team, in a test environment.

A02: Security Misconfiguration

Up from fifth place in 2021. Modern applications are mostly configuration: frameworks, containers, cloud services and feature flags, and every one has defaults that may not be safe.

  • Default accounts, sample apps or admin consoles left enabled.
  • Directory listing, debug endpoints or open cloud storage.
  • Missing security headers such as Content-Security-Policy, X-Content-Type-Options: nosniff and frame protection.
  • Over-permissive CORS, for example reflecting any Origin while allowing credentials.
  • Different settings between environments, so the hardening you tested in staging is not what runs in production.

Header checks are cheap to automate on key pages:

import { test, expect } from '@playwright/test';

test('home page sends baseline security headers', async ({ request }) => {
  const res = await request.get('/');
  const headers = res.headers(); // header names are lower-cased
  expect(headers['strict-transport-security']).toBeTruthy();
  expect(headers['x-content-type-options']).toBe('nosniff');
  expect(headers['content-security-policy']).toBeTruthy();
});

The HTTP headers reference explains what each header does. Verbose error pages used to be listed mainly here; in 2025 the wider problem of handling errors badly has its own category, A10.

A03: Software Supply Chain Failures

New in 2025. It broadens 2021’s “Vulnerable and Outdated Components”: the risk is not only a known-vulnerable library, but anything in the process of building, distributing or updating your software that is broken or compromised. That includes direct and transitive dependencies, package registries, build tools, CI/CD pipelines, container images and developer tooling, whether the problem is an accidental vulnerability or a deliberate malicious change.

Most of this is owned by developers and platform engineers, but testers own a test framework, a CI job and a pile of dependencies too, and can check that the controls exist and work.

  • Dependency and lockfile review: a lockfile (package-lock.json, pnpm-lock.yaml, poetry.lock and similar) is committed, and CI installs from it exactly (for npm, npm ci rather than npm install). In code review, a pull request that changes the lockfile without a matching change to the manifest deserves a question. Unused dependencies should be removed, not left to age.
  • Pinned versions and integrity: dependencies and base images are pinned to specific versions (or digests for container images) rather than floating tags such as latest. Lockfiles record integrity hashes, and installs should fail if a downloaded package does not match. Third-party scripts loaded in the browser can carry a Subresource Integrity integrity attribute.
  • SBOM: the team can produce a Software Bill of Materials listing every component and version in a release. Ask for it, and check it can answer “are we affected?” when a new advisory lands.
  • Scanning in the pipeline: software composition analysis runs on every change and on a schedule, because new advisories appear for code you have not touched. npm audit, GitHub’s Dependabot alerts, OWASP Dependency-Check and commercial SCA tools all do this in broadly similar ways: compare your resolved dependency tree against databases of known vulnerabilities. Check that results are visible, that there is an agreed policy per severity, and that the test framework’s own dependencies are included.
  • CI permissions: pipeline tokens have the least privilege they need (read-only by default, write only for the jobs that publish); secrets are not exposed to builds triggered from untrusted forks; nobody can push to production without review. Branch protection and required reviews are part of this.
  • Trusted sources: packages come from the official registry or your approved internal mirror, not from a random URL in an install script.

A04: Cryptographic Failures

Ask what data is sensitive (passwords, payment details, health data, personal data) and whether it is protected in transit and at rest.

  • Is every page and API served over HTTPS, and does HTTP redirect to HTTPS? Is the Strict-Transport-Security header set?
  • Do responses include data the client does not need, such as full card numbers, password hashes or other users’ emails?
  • Are tokens or personal data placed in URLs, where they end up in logs and browser history?
  • Can you see sensitive values in logs, error messages or analytics payloads?

Storage-level checks (hashing algorithms, key management) usually need code review or a security specialist; testers can still flag what they observe.

A05: Injection

Untrusted input reaching an interpreter: SQL, NoSQL, OS commands, LDAP, or the browser’s HTML and JavaScript engine (cross-site scripting, XSS, is part of this category).

  • Include special characters in your normal test data: quotes, angle brackets, semicolons, unicode, very long strings. A test string generator helps.
  • Watch for symptoms rather than trying to exploit: a 500 error or a database error message when a field contains a single quote is a finding.
  • For XSS, check that user-supplied text is displayed as text. Entering a harmless marker such as <b>bold-test</b> in a profile name and seeing bold text on another page indicates the output is not encoded.

The fix is on the developer side (parameterised queries, output encoding, safe APIs), but testers are well placed to find where input is reflected and stored.

A06: Insecure Design

Flaws that exist even with perfect code, because the design did not consider abuse. Examples: a password reset flow that reveals whether an email is registered; a coupon that can be applied unlimited times; no limit on how many times a one-time code can be guessed.

Testers contribute here in refinement and design reviews: ask “how could this be abused?” for every feature and turn the answers into acceptance criteria. Business-logic abuse cases are often found by testers rather than scanners.

A07: Authentication Failures

Renamed from 2021’s “Identification and Authentication Failures”; the test ideas are the same.

  • Is there rate limiting or lockout on login and one-time-code endpoints?
  • Do sessions expire after inactivity and after logout? Does the old session token still work after logout or password change?
  • Are weak or known-breached passwords rejected, per the team’s policy?
  • Do error messages reveal whether the username or the password was wrong?
  • Are session cookies marked Secure, HttpOnly and with an appropriate SameSite value?

For token-based APIs, decode a JWT with a JWT decoder to check its expiry and claims, and verify that tampering with the payload is rejected by the server.

A08: Software or Data Integrity Failures

Trusting things whose integrity was not verified: unsigned updates, third-party scripts loaded from CDNs without integrity checks, or insecure deserialisation of data from clients. It overlaps with A03, but where A03 is about the components and pipeline you depend on, A08 is about the application accepting code or data it has not verified. Testers can check whether client-supplied data that should be server-controlled (prices, roles, flags in hidden fields or cookies) is accepted when modified.

A09: Security Logging and Alerting Failures

Renamed from “Logging and Monitoring Failures”, and the new name makes the point: logs nobody acts on do not help. Can the team tell when it is under attack? Test that failed logins, access-control denials and high-value actions produce log entries with enough context (who, what, when), that logs do not contain secrets, and that alerts fire in a test environment when thresholds are crossed. This is ordinary testing applied to an often-ignored requirement.

A10: Mishandling of Exceptional Conditions

New in 2025. OWASP describes it as software failing to prevent, detect or respond properly to unusual situations: bad input, a dependency timing out, a full disk, a step in a multi-step process failing halfway. The result can be crashes, information leaks, inconsistent state or security checks that are silently skipped. Testers already hunt for edge cases, so this category rewards exactly the habits you have.

What to look for:

  • Verbose errors that leak internals: stack traces, framework and server versions, SQL fragments, file paths or internal hostnames in responses. Attackers deliberately trigger errors to collect this detail for a more targeted attack.
  • Fail open versus fail closed: when something goes wrong, does the system deny by default or carry on? An authorisation service timing out should mean “access denied”, not “access granted”. A payment or transfer that fails part-way should roll back completely, not leave the order paid but unshipped, or the money moved twice.
  • Unhandled edge cases: empty bodies, malformed JSON, wrong content types, nulls, huge numbers, negative quantities, unexpected enum values, duplicate submissions and concurrent requests. Each should produce a controlled response, not a 500 or a half-finished operation.
  • Resource exhaustion: errors that leave files, connections or locks held. Repeat a failing upload or request many times (in an agreed environment) and check the service recovers and resources are released. Rate limits and quotas belong here too.
  • Consistent error formats: errors should come from one central handler in a predictable shape. For HTTP APIs, RFC 9457 (Problem Details for HTTP APIs) defines a standard application/problem+json body with fields such as type, title, status and detail. A consistent format is easier to test, and stray responses in a different format often point to a code path the global handler missed.

How a tester probes it: break things on purpose. Send malformed input, interrupt flows (close the tab mid-checkout, retry a request after a timeout), ask the team to stop a downstream dependency in a test environment, and observe both the response and the resulting state. Check the logs too: an error the user never sees may still be one the team should be alerted to (A09).

A cheap automated check for the most visible symptom:

import { test, expect } from '@playwright/test';

test('malformed JSON gets a clean 400 without internal details', async ({ request }) => {
  const res = await request.post('/api/orders', {
    headers: { 'Content-Type': 'application/json' },
    data: '{"items": [', // deliberately truncated
  });
  expect(res.status()).toBe(400);

  const body = await res.text();
  expect(body).not.toMatch(/Exception|Traceback|SQLSTATE|node_modules/i);

  // If the team has adopted RFC 9457 problem details:
  expect(res.headers()['content-type']).toContain('application/problem+json');
});

Adjust the patterns to your stack, and agree the error contract with developers before you assert on it.

Where testers fit and where to stop

Practical habits:

  • Add an “abuse cases” section to test plans for features that handle money, identity, personal data or file uploads.
  • Add a “what if it fails halfway?” question to the same plans.
  • Seed test data with at least two users per role so access-control tests are always possible.
  • Treat security findings as sensitive: report them through the agreed channel, not in public tickets or chat channels.
  • Run automated scanners such as OWASP ZAP only against environments where that is approved.

Practice exercise

You are testing a new “Download invoice” feature: GET /api/invoices/{id}/pdf returns a PDF for the logged-in customer. List test ideas mapped to OWASP Top 10:2025 categories.

Show a model answer
  • A01 Broken Access Control: as customer A, request customer B’s invoice ID; request with no token, an expired token and a token from a logged-out session; try sequential IDs to see whether invoices are enumerable; check whether support or admin roles have the intended access and no more. If the PDF renderer loads images or fonts from URLs derived from user data, raise SSRF with the security team for review.
  • A02 Security Misconfiguration: caching headers prevent invoices being cached by shared proxies; the PDF library’s debug or preview endpoints are not exposed.
  • A03 Software Supply Chain Failures: the PDF generation library is pinned, in the lockfile and covered by dependency scanning; it appears in the SBOM.
  • A04 Cryptographic Failures: the endpoint is HTTPS only; the PDF and response do not include more personal data than needed; the invoice URL is not a long-lived public link containing a token that leaks via logs.
  • A05 Injection: customer-controlled fields (name, address) that appear in the PDF are rendered as text.
  • A07 Authentication Failures: access stops after logout and after a password change.
  • A09 Security Logging and Alerting Failures: denied access attempts are logged with user and invoice ID, and repeated denials raise an alert.
  • A10 Mishandling of Exceptional Conditions: non-numeric, negative and very large IDs return a clean 400 or 404, not a 500 or a stack trace; if PDF generation fails (for example a missing template or a renderer timeout in a test environment), the customer gets a consistent error and no partial or empty file; repeated failures do not leave the service degraded.

Automate the A01 and A10 checks at API level with two seeded customers, and run them on every PR.

Key takeaways

  • The OWASP Top 10 is an awareness ranking, not a full standard; the 2025 edition runs from A01 Broken Access Control to A10 Mishandling of Exceptional Conditions.
  • Two categories are new in 2025: Software Supply Chain Failures (broadening “Vulnerable and Outdated Components”) and Mishandling of Exceptional Conditions. SSRF now sits under Broken Access Control.
  • Broken access control is the category testers can find most often, by switching users, roles and methods.
  • Build abuse cases and failure cases into normal test design, and automate cheap checks at the API level.
  • Recognise symptoms (500s on special characters, stack traces, missing headers, fail-open behaviour) rather than trying to exploit them.
  • Only test authorised systems within agreed scope, and report findings through secure channels.

Key terms

Finished this lesson?

Progress is saved in this browser. No account needed.

Type to search across SDETRole.

Full-text search by Pagefind · runs in your browser Open search page →