CH9: Web Application Attacks and the OWASP Top 10 (2025)
Introduction
Web applications are exposed to the internet on purpose. A company puts its storefront, its customer portal, its internal dashboards, and its application programming interfaces (APIs) online because that is how the business runs. That same exposure makes web applications one of the most frequently attacked surfaces in any engagement. A web application pentester spends a large share of their time here, and so do the attackers they simulate.
Earlier chapters attacked the network and the host. Web application attacks work at a different layer. They operate inside the intended functionality of the application. An attacker sends a database query hidden in a form field. They change a number in a URL to read another customer's record. They upload a file the server was never meant to execute. The system is often working exactly as written. The flaw is in the logic, the configuration, or the assumptions the developers made about the input they would receive.
This chapter is organized around the OWASP Top 10 (2025), the industry's most widely referenced catalog of web application risk. Rather than list attacks in isolation, the chapter walks the ten categories in their official order, explains how a tester finds and exploits each one, and names the tools most commonly used against it. Employers in application security expect familiarity with these ten categories by name and by number, so learning the framework is part of learning the job.
The chapter explains how OWASP compiles the Top 10, what each 2025 category covers, and which conditions create each class of vulnerability. Fictional examples trace representative attacks from input to impact, connect tools to specific assessment tasks, and explain the defenses that address each failure.
Learning Objectives
The chapter develops the following skills:
- Explain what the Open Worldwide Application Security Project (OWASP) is, how the Top 10 is built from data and community input, and how the 2025 edition differs from 2021.
- Describe each of the ten 2025 categories and identify the conditions in an application that create each one.
- Replay an API request with Postman or curl, vary one input or identity at a time, and assess authorization using known object ownership and response content.
- Trace path traversal and SSRF through the filesystem or network boundary they cross, and identify the permissions that limit their impact.
- Inspect approved cloud storage for unintended anonymous read, listing, or write access, distinguishing each permission from the others.
- Explain a normal database-backed login, identify how SQL injection changes its query, and distinguish login bypass, data disclosure, and data alteration.
- Trace command injection from a form field into a shell and explain how the web process's privileges affect access to account data and password hashes.
- Compare reflected and stored XSS by input source, processing location, execution, and appropriate output control.
- Select testing tools, document reproducible evidence during an authorized assessment, and recommend a control that addresses the demonstrated failure.
9.1 What OWASP Is and How the Top 10 Is Built
The Open Worldwide Application Security Project (OWASP) is a nonprofit foundation that produces free, community-driven resources for software security. It is vendor-neutral, and its material is open to anyone. Its resources include the Web Security Testing Guide (WSTG), the Application Security Verification Standard (ASVS), the Cheat Sheet Series, and the API Security Top 10. These resources provide recognized methods and requirements for assessing application security.
What the Top 10 actually is
The OWASP Top 10 is an awareness document. It ranks the ten most significant categories of web application security risk so that developers, testers, and managers can agree on priorities. Each entry is a category of related weaknesses rather than a single bug. "Injection," for example, groups Structured Query Language (SQL) injection, command injection, and several other flaws that share the same root cause. Categories are labeled with an identifier such as A01:2025, where the number is the rank for that edition and the year identifies the edition.
The Top 10 is not a complete testing checklist and it is not a certification standard. For a thorough test, professionals pair it with the more detailed OWASP Web Security Testing Guide and the Application Security Verification Standard. Think of the Top 10 as the shared vocabulary the whole industry uses, and the WSTG and ASVS as the step-by-step procedures.
How the list is compiled
OWASP builds the list from two sources. Eight of the ten categories are chosen from contributed data. Security firms and organizations submit anonymized findings covering hundreds of thousands of applications, and OWASP analyzes how often each type of weakness appears and how it maps to the Common Weakness Enumeration (CWE), a standardized catalog of software weakness types maintained by MITRE. The remaining two categories come from a community survey, which lets practitioners flag risks they see rising in the field before the data fully reflects them. This mix keeps the list grounded in real findings while still capturing emerging concerns. The 2025 edition is the eighth installment of the list.
Ranking reflects a combination of how widespread a weakness is, how easy it is to exploit, and how much damage it causes. A category can move up the list because it became more common, because tooling made it easier to exploit, or because its impact grew.
What changed from 2021 to 2025
The 2025 edition changes several categories from the 2021 edition:
- Broken Access Control (A01) remains the top category. It is the most consistently reported class of web weakness.
- Security Misconfiguration moved up to A02 from fifth place, reflecting how often modern cloud-connected applications ship with unsafe defaults or exposed settings.
- Software Supply Chain Failures (A03) is an expanded category. It grew out of the older "vulnerable and outdated components" entry to cover the fuller risk of trusting third-party code, build systems, and dependencies.
- Cryptographic Failures (A04) and Injection (A05) are still present but ranked lower than in 2021.
- Authentication Failures (A07) is the shortened name for what 2021 called "identification and authentication failures."
- Security Logging and Alerting Failures (A09) is the renamed logging category, with "alerting" replacing "monitoring" to stress that recording an event is only useful if something responds to it.
- Mishandling of Exceptional Conditions (A10) is new. It covers what an application does when something goes wrong: errors, unexpected input, and failure states that are handled insecurely.
- Server-Side Request Forgery (SSRF) moved into Broken Access Control (A01).
9.2 A01:2025 Broken Access Control
Access control decides which resources a requester may read or change. A successful login establishes an identity. Each subsequent request still needs an authorization decision. A customer may read their own invoice, while a billing employee may read invoices for an assigned group of customers. The server must enforce those differences even when someone sends requests outside the normal interface.
All worked examples are fictional. Portal addresses such as https://portal.northstar.test, accounts, credentials, cloud resources, and displayed outputs are invented to illustrate how the attacks work.
Repeating API requests with Postman and curl
An application programming interface (API) exposes operations that another program can request. A web page might ask /api/invoices/5001 for invoice data, then arrange the returned fields on the screen. An endpoint is a particular API address and operation. The browser, Postman, and curl can all send the same request to that endpoint.
A Hypertext Transfer Protocol (HTTP) request has a method such as GET, a path naming the resource, headers carrying request metadata, and sometimes a body carrying submitted data. HTTPS protects HTTP traffic with Transport Layer Security (TLS). GET normally retrieves a resource. POST commonly submits data for processing. The server returns a status code, headers, and a response body. JavaScript Object Notation (JSON) is a text format that represents values as named fields, lists, and other simple structures.
Suppose Alice owns invoice 5001 and Bob owns invoice 5002. Alice's request looks like this:
GET /api/invoices/5001 HTTP/1.1
Host: portal.northstar.test
Authorization: Bearer EXAMPLE_ALICE_TOKEN
Accept: application/json
A bearer token is a credential accepted from whoever presents it. EXAMPLE_ALICE_TOKEN is a fictional value representing Alice's credential. The expected response contains Alice's invoice:
{"invoice_id":5001,"owner":"alice","total":"84.00"}
Postman provides a graphical API client. The invoice example uses these settings and comparisons:
- Request:
GET https://portal.northstar.test/api/invoices/5001, with an empty body. - Identity: Bearer Token in the Authorization controls, with
EXAMPLE_ALICE_TOKENas the value. The Headers tab includesAccept: application/json. - Baseline: The first response contains Alice's invoice. Its status and body provide the comparison point for the next request.
- Repeat: A duplicate request changes only
5001to5002, keeping Alice's token. The expected result is denial because invoice 5002 belongs to Bob. - Controls: Bob's token should allow access to invoice 5002; an anonymous request should be denied.
curl is a command-line HTTP client. These Windows PowerShell examples express the same two requests using curl.exe. The equivalent executable on Linux or macOS is curl:
curl.exe --include --header "Authorization: Bearer EXAMPLE_ALICE_TOKEN" --header "Accept: application/json" "https://portal.northstar.test/api/invoices/5001"
curl.exe --include --header "Authorization: Bearer EXAMPLE_ALICE_TOKEN" --header "Accept: application/json" "https://portal.northstar.test/api/invoices/5002"
--include displays response headers with the body. --header adds a request header. Neither command has a body, so curl sends GET by default. The quotes keep the address and each header together as one argument. curl does not enforce the portal's ownership policy. It sends the request and shows the response. See the curl manual.
An Insecure Direct Object Reference (IDOR) occurs when a client-controlled identifier reaches an object without an adequate authorization check. API guidance calls this Broken Object Level Authorization (BOLA). An identifier can be a number, a filename, or a long random string. Unpredictable identifiers reduce guessing but do not establish permission.
The same weakness can be tested through an ordinary browser without Postman or curl. Suppose a user profile page uses an address such as https://portal.northstar.test/profile?id=1001. While signed in as Alice, record the page for Alice's own profile, then change only the number in the address to 1002 and load the page. If 1002 belongs to Bob, the application should deny the request or return a deliberately non-disclosing response. If the page displays Bob's name, email address, or other private fields, Alice can access another user's object by changing a URL value. This is an authorization failure: Alice is authenticated, but the server did not check whether she was allowed to read profile 1002.
Next, open a private or incognito browser window, do not sign in, and request the same profile URL directly. A redirect to login or a denial response is the expected result. If the profile loads without a session, that is an authentication failure because the application did not require an identity before returning private data. Test the two conditions separately and record the starting identity, the original and modified URLs, whether a session was present, and the fields shown in each response. Use accounts and profile records approved for the assessment, and avoid changing profile data during a read-only check.
Figure 9.1 illustrates the signed-in profile comparison and the separate request without a session.
Figure 9.1: Changing the profile ID while retaining Alice's session tests authorization. Requesting the profile without a session separately tests authentication. The browser views are fictional.
Text description of Figure 9.1
First, Alice views her own private profile at /profile?id=1001. She changes only the ID to 1002 and remains signed in as Alice. The expected response denies access to Bob's private profile. If Bob's private data appears, the application has failed to enforce object authorization, producing an insecure direct object reference. Separately, a private browser window with no sign-in session requests /profile?id=1002. A login redirect or denial is expected. Returning Bob's private profile without requiring a session demonstrates missing authentication. These checks compare responses and do not change profile data.
An HTTP 200 alone cannot prove IDOR. Some applications return error messages with that status. Inspect the body and prove that Alice received Bob's private data. Similarly, a denial response proves only that particular request was denied. Document the identity, object, operation, and observed result rather than inferring the behavior of every endpoint. Figure 9.2 shows the two-request comparison and the expected ownership decision.
Figure 9.2: A controlled API comparison preserves Alice's token while changing the requested invoice. The illustrated responses show intended authorization behavior. This is a representative workflow, not a Postman screenshot.
Text description of Figure 9.2
The first request uses Alice's token to retrieve invoice 5001, which belongs to Alice. The second changes only the object identifier to 5002, which belongs to Bob. The API should deny that request. A finding would require evidence that the second response contained Bob's private invoice. Status codes alone do not prove authorization behavior.
Replay also helps test write operations and business rules. For example, sending POST /api/coupons/redeem twice with the same single-use coupon should produce only one discount. Two discounts would reveal a failure to enforce single use. Payment and order-creation endpoints often need an explicit duplicate-request control because replaying a state-changing request can create an additional transaction.
Path traversal: escaping a download directory
Path traversal, also called directory traversal, lets input change the filesystem location an application accesses. Consider a portal that serves manuals from /srv/portal/downloads. The application receives a file parameter and builds a path by joining that directory with the supplied text.
Most web servers run on top of an operating system: common Linux deployments use Apache or Nginx, while Windows deployments may use Internet Information Services (IIS). A path-traversal attack attempts to escape the directory intended for web content and reach the underlying filesystem that the web service runs on, limited by the files and permissions available to that service.
Normal request: /download?file=guide.pdf
Constructed path: /srv/portal/downloads/guide.pdf
Traversal request: /download?file=../../../etc/passwd
Constructed path: /srv/portal/downloads/../../../etc/passwd
Resolved path: /etc/passwd
The path component .. means the parent directory. Starting in downloads, the first .. reaches portal, the second reaches srv, and the third reaches /, the filesystem root. The remaining etc/passwd selects a file beneath that root. Figure 9.3 traces each step.
Figure 9.3: Three parent-directory components escape the intended download directory before the application selects the account file.
Text description of Figure 9.3
The first parent step moves from downloads to portal, the second from portal to srv, and the third from srv to the root. The remaining path selects etc and then passwd. The normal download, guide.pdf, remains inside downloads. The application can disclose the account file only if its process can read it.
If the download code opens this resolved path and returns its bytes, the response contains account information instead of a manual. On a typical Linux system, /etc/passwd is readable by ordinary processes and contains account names and identifiers. It normally uses an x marker where historical password data once appeared. Section 9.6 explains its fields and the separately protected /etc/shadow file.
Traversal can also expose application configuration, source files, or backups that the web process can read. A write-capable feature might allow file replacement instead. The operation implemented by the endpoint and the process's filesystem permissions determine the impact. Successfully reading a file does not demonstrate command execution or access to the entire host.
Server-Side Request Forgery: making the server fetch a destination
Server-Side Request Forgery (SSRF) occurs when an attacker influences a network request made by the application server. A portal might offer a link-preview feature that fetches a supplied Uniform Resource Locator (URL), extracts its title, and displays a preview. That feature introduces two separate requests:
- The client submits a URL to the portal.
- The portal acts as an HTTP client and requests that URL from its own network position.
For an intended use, the target might be an approved supplier's public catalog. In this example, the submitted target is http://127.0.0.1:9000/status. The address 127.0.0.1 is a loopback address, which refers to the machine or network namespace making the request. Here it refers to the portal's runtime environment. A vulnerable fetcher can therefore contact a diagnostic service that is not exposed to the attacker's browser.
curl.exe --get --data-urlencode "url=http://127.0.0.1:9000/status" "https://portal.northstar.test/api/preview"
--get places the encoded parameter in the URL query string. --data-urlencode handles characters inside the nested URL. If the portal returns the internal response, it might look like this:
{"service":"invoice-worker","queue_depth":3}
This response demonstrates disclosure of internal service information. It does not establish access to cloud credentials. Figure 9.4 separates the originating client request from the server's new request.
Figure 9.4: SSRF causes the portal to make a second request from its own network position. In this example, the preview endpoint returns an internal service's response.
Text description of Figure 9.4
The client submits a URL to the preview endpoint. The portal makes a request to an internal service from within the server environment. That service returns a response to the portal, which exposes the content in its preview response to the original client. The client has no direct connection to the internal service in this sequence. Loopback refers to the runtime environment making the request.
SSRF impact depends on where the server can connect, which methods and headers the attacker can influence, and what the destination accepts. Internal services that trust network location can expose administrative actions. Cloud instance metadata services provide information about a virtual machine and may provide temporary credentials associated with its assigned role. Some platforms use the link-local address 169.254.169.254, but the exact endpoint and protections differ. Amazon's Instance Metadata Service version 2 (IMDSv2) requires a token exchange, which changes what a simple URL-only fetch can accomplish.
The developer-side fix
Enforce authorization on every server-side operation using the authenticated identity, object ownership, and required action. Deny requests unless a rule permits them. Keep file access within the intended filesystem boundary and outbound requests within the intended network boundary. These checks protect different resources and need separate tests.
9.3 A02:2025 Security Misconfiguration
Security misconfiguration is an insecure setting in the application, framework, web server, operating system, or supporting cloud service. A sound feature can still expose data through an overly broad storage policy or an enabled diagnostic endpoint.
Common exposures
Testers look for:
- Unchanged default credentials: Factory usernames or passwords that were never replaced.
- Directory listings: Web-server indexes that reveal files and subdirectories intended to remain hidden.
- Exposed administrative interfaces: Management pages or endpoints reachable beyond their intended audience.
- Sample applications: Demonstration code or setup pages that were left enabled in a deployed system.
- Verbose error messages: A stack trace, the sequence of program calls that led to an error, may reveal internal paths or implementation details.
- Missing or weak browser security headers: Content Security Policy constrains which content a page may execute, while HTTP Strict Transport Security tells browsers to use HTTPS for the site after they learn the policy.
Exposures can combine. A published backup might contain a configuration file, which contains a credential usable against another application. Confirm each link with authorized evidence rather than assuming that finding a filename proves compromise.
Finding misconfigured public cloud storage
Object storage stores files as named objects inside a bucket or container. Amazon Simple Storage Service (S3), Azure Blob Storage, and Google Cloud Storage provide this model. A browser may fetch product photographs directly from object storage without going through the portal's login checks. That design requires a separate storage access policy.
Public access can be intentional, as with a brochure or product image. The finding is unintended exposure of a protected resource or operation. Check these permissions separately:
| Operation | What it permits | Possible impact |
|---|---|---|
| Read a known object | Retrieve a particular file when its address is known | Disclosure of an invoice export or backup |
| List objects | Obtain names and metadata for objects in a bucket or container | Discovery of backups, customer identifiers, or internal naming |
| Write or replace objects | Create or change stored content | Website defacement, altered downloads, or application data corruption |
A bucket can deny listing while allowing a known object to be read. A readable object does not establish permission to write. Figure 9.5 separates the checks.
Figure 9.5: Storage assessment combines owner-side policy review with separately scoped anonymous access checks. Public asset delivery can be intentional.
Text description of Figure 9.5
An owner inventory provides resource identity and policy evidence. An anonymous client tests the specifically approved resource without signing credentials. Read access retrieves a known object, listing reveals object names, and write access permits content changes. One permission does not establish the others. A public brochure can be intended while a public customer export is an exposure.
Start discovery with the organization's authorized inventory and the application itself. Browser developer tools can show storage addresses used for images and downloads. In-scope HTML, JavaScript, configuration, and asset links may reveal additional names. Confirm ownership and scope before testing a newly discovered address. A name resembling the client's brand may belong to someone else.
Use tools according to the evidence needed:
- Provider inventory and policy tools: AWS Identity and Access Management (IAM) Access Analyzer for S3 identifies bucket policies or access controls that permit external access. Review the finding to distinguish public access from an intended external account. Azure's storage controls separate account-level permission to allow anonymous access from container-level public access. Google Cloud IAM policy review identifies public principals such as
allUsers. - Provider command-line interfaces (CLIs): The AWS CLI, Azure CLI, and Google Cloud CLI let an authorized owner inspect the actual account and resource settings. Owner credentials can reveal policies that an outside tester cannot see.
- Discovery scanners: nuclei templates and tools such as
cloud_enumand S3Scanner can generate or test candidate cloud-storage bucket names and access. Limit names, providers, requests, and rate to the approved assessment. Thecloud_enummaintainer currently encourages moving this functionality into nuclei templates, so review maintenance status and template behavior before selecting a tool for an assessment. - Anonymous verification: curl or a provider client operating without credentials can test whether a specific approved resource is publicly accessible. A successful request signed with the owner's credentials says nothing about anonymous access.
Browser Security Headers
Browser security headers let a server give the browser security instructions along with a response. They reduce the impact of several web attacks, but they do not replace authentication, authorization, input validation, or safe output handling. The correct policy depends on the application; a header that is useful for a public brochure site may break a complex application if copied without review.
| Header | What it controls | Attack or weakness it helps mitigate |
|---|---|---|
Content-Security-Policy (CSP) |
Which scripts, styles, images, frames, and other content the browser may load or execute | Reduces the impact of XSS and some malicious-content injection by restricting executable sources. A strong policy should avoid unnecessary inline script and broad wildcards. CSP cannot repair unsafe server-side authorization or make unsafe HTML construction safe by itself. |
Strict-Transport-Security (HSTS) |
Tells a browser to use HTTPS for future requests to the site | Helps prevent HTTP downgrade and SSL-stripping attacks after the browser has received the policy over a secure connection. It does not encrypt an initial insecure visit unless the site is also covered by an appropriate preload policy. |
Content-Security-Policy: frame-ancestors or X-Frame-Options |
Controls which pages may embed the response in a frame | Helps mitigate clickjacking, where an attacker places an application's buttons beneath deceptive content. frame-ancestors is the more flexible modern control; X-Frame-Options remains useful for older browser support. |
Referrer-Policy |
Limits how much of the current URL a browser sends in the Referer header when following a link or loading a resource |
Reduces leakage of paths, query strings, or other URL data to third parties. It is especially important when URLs may contain identifiers or sensitive parameters. |
Permissions-Policy |
Restricts browser features such as camera, microphone, geolocation, and fullscreen | Limits the browser capabilities available to a page or embedded origin, reducing the impact of compromised content or an untrusted frame. It does not grant permission by itself; users and browser prompts still apply. |
Access-Control-Allow-Origin and related CORS headers |
Which other origins may read selected cross-origin responses through browser JavaScript | Limits cross-origin data theft through overly broad CORS policies. Do not use * with credentials, and allow only the origins and methods the application actually needs. CORS is a browser read-control, not a replacement for authentication or authorization. |
Testers should inspect these headers on representative pages and workflows, not only the home page. Record the exact policy, the response it protects, and the behavior observed in a browser. A missing header is not automatically a vulnerability: for example, framing may be intentional for a trusted portal, and a public image may not need the same policy as an authenticated account page. The finding should connect the header weakness to a reachable attack or meaningful exposure.
Other testing tools
- Nikto: Checks for exposed files, default content, and unsafe server options.
- Nmap scripting engine: Examines web-service behavior with targeted scripts.
- nuclei: Executes selected detection templates.
- ffuf and gobuster: Discover candidate files and directories such as
backup.zipor.git. - Burp Suite and ZAP: Support inspection of headers, cookies, and server responses.
Review the scope and behavior of active checks before running them because some templates send large numbers of requests or attempt exploitation.
The developer-side fix
Use a reviewed configuration baseline, remove unused features, protect diagnostic interfaces, and return limited error details to users. Separate public assets from private exports and backups. In AWS, S3 Block Public Access can override policies or access controls that would otherwise grant public access. Azure and Google Cloud offer corresponding account, container, bucket, and organizational controls. Verify effective access after configuration changes and monitor for drift.
9.4 A03:2025 Software Supply Chain Failures
Modern applications are assembled more than they are written. A typical project pulls in hundreds of third-party libraries, base container images, and build tools. Software supply chain failures cover the risk that comes with trusting all of that outside code and infrastructure. A single vulnerable library, a compromised build pipeline, or a malicious package can put attacker code inside an application the developers believe they wrote themselves. This category expanded in 2025 from the older focus on outdated components to the broader chain: dependencies, the systems that build and deliver software, and the trust placed in each link.
How it is exploited
- Known-vulnerable dependencies. An application uses a library version with a published vulnerability. The attacker looks up the version, finds the matching exploit, and uses it. The Log4Shell vulnerability in the Log4j logging library is a well-known example of one dependency exposing a huge number of applications.
- Malicious packages. An attacker publishes a package with a name close to a popular one (typosquatting) or slips malicious code into an existing package, and developers install it without noticing.
- Compromised build pipeline. An attacker who reaches the continuous integration and continuous delivery (CI/CD) system can inject code into builds that are then signed and shipped as trusted.
From a tester's point of view, the practical work is identifying the components an application uses and their versions, then determining which have known vulnerabilities that are actually reachable in this deployment.
Tools most commonly used
- OWASP Dependency-Check and Dependency-Track: Inventory an application's libraries and match them against known vulnerability databases.
- retire.js: Finds vulnerable JavaScript libraries loaded by a site.
- Package-manager auditing: Tools such as
npm auditcheck Node.js project dependencies for known vulnerabilities. - Trivy and Grype: Scan container images and filesystems for vulnerable packages.
- Syft: Generates a Software Bill of Materials (SBOM), a formal inventory of everything a build contains.
- OSV-Scanner: Checks dependencies against the Open Source Vulnerabilities database.
- searchsploit: Uses the offline Exploit-DB index to help locate a working exploit after a vulnerable component has been identified.
The developer-side fix
- Maintain a dependency inventory: Track the libraries, tools, and other components used by each application.
- Keep components patched: Update dependencies when security fixes are available.
- Verify package sources and integrity: Pull packages only from trusted sources and validate their integrity before use.
- Generate and review an SBOM: A Software Bill of Materials makes the contents of the supply chain visible.
- Secure the development pipeline: The National Institute of Standards and Technology (NIST) Secure Software Development Framework (SSDF, Special Publication 800-218) provides recognized practices for this work.
9.5 A04:2025 Cryptographic Failures
Cryptographic failures cover the mishandling of data that should be protected, whether in transit across the network or at rest in storage. The failure is sometimes the absence of encryption and sometimes the use of encryption that is weak, misconfigured, or implemented incorrectly. The impact is exposure of sensitive data such as passwords, payment details, health records, or session tokens.
How it is exploited
- Cleartext transmission. An application serves a login page or an API over unencrypted HyperText Transfer Protocol (HTTP) instead of HTTPS, so anyone positioned on the network path can read credentials and session cookies as they pass.
- Weak or outdated protocols and ciphers. A server still accepts old Transport Layer Security (TLS) versions or weak cipher suites, which can be downgraded and attacked.
- Weak password storage. Passwords stored with a fast or unsalted hash, such as unsalted Message Digest 5 (MD5), can be cracked quickly if the database is stolen.
- Hardcoded or exposed keys. Encryption keys or secrets left in source code, configuration files, or client-side JavaScript remove the protection entirely.
A tester in this category often works alongside the network attacks from Chapter 7. Capturing traffic on an unencrypted endpoint, then confirming that credentials pass in cleartext, is a direct demonstration of the risk.
Tools most commonly used
- testssl.sh, sslscan, and sslyze: Inspect a server's TLS configuration and report weak protocol versions, weak ciphers, and certificate problems.
- Nmap: Uses the
ssl-enum-ciphersscript to examine TLS configuration from the scanning workflow. - Wireshark: Captures and displays traffic, making cleartext transmission visible.
- hashcat and John the Ripper: Test recovered password hashes against wordlists and rules to evaluate the strength of the storage scheme.
- CyberChef: Helps testers decode, analyze, and recognize encoding and weak cryptographic formats.
The developer-side fix
Sensitive data should be encrypted in transit with current TLS and encrypted at rest where appropriate. Passwords should be stored with a slow, salted algorithm designed for the purpose, such as bcrypt, scrypt, or Argon2. Keys and secrets belong in a managed secret store, never in source code. Classifying which data is sensitive is the first step, because you cannot protect what you have not identified.
9.6 A05:2025 Injection
An interpreter reads text and gives it meaning as instructions. A database interprets SQL, a shell interprets operating-system commands, and a browser interprets HTML and JavaScript. Injection occurs when an application places untrusted input into an instruction and lets that input change what the interpreter does.
Each injection example has four parts to trace: where the input enters, how the application constructs the instruction, which interpreter receives it, and how the result reaches the user. The same text can be harmless in one context and executable in another.
SQL injection: following a login from form to database
Structured Query Language (SQL) tells a relational database which records to retrieve or change. A table organizes records into rows and named columns. A query is a database instruction. SQL injection (SQLi) lets submitted data alter that instruction.
Step 1: identify the records and the intended decision
Consider a login portal whose users table stores readable passwords. This unsafe design makes the query's comparison easy to follow. This is also a password-storage failure. A production design should store password hashes and verify them in application code, as shown later in this section.
| id | username | password | role |
|---|---|---|---|
| 1 | admin | AdminExample!42 | admin |
| 7 | alice | AliceExample!42 | customer |
The intended rule is that the supplied username and password must match the same row. The application creates a session, a continuing record of the authenticated user, only after that check succeeds.
Step 2: follow a normal submission
Alice types alice and AliceExample!42 into the form. The browser sends an HTTP request to the application, not directly to the database:
POST /login HTTP/1.1
Host: portal.northstar.test
Content-Type: application/x-www-form-urlencoded
username=alice&password=AliceExample%2142
The form encoding represents ! as %21. The framework decodes the fields before the login handler uses them. In this unsafe Python fragment, concatenation, joining strings end to end, inserts both field values into the SQL text:
# Deliberately vulnerable teaching fragment. Query is built as one line.
sql = "SELECT id, username, role FROM users WHERE username = '" + username + "' AND password = '" + password + "';"
row = connection.execute(sql).fetchone()
if row is not None:
create_session(user_id=row[0])
connection.execute sends SQL to the database. fetchone() retrieves one result row, if a row exists. create_session represents the application's session-management function. With Alice's normal input, the resulting query is:
SELECT id, username, role FROM users WHERE username = 'alice' AND password = 'AliceExample!42';
Read this statement in parts:
SELECT id, username, rolerequests three columns from matching rows.FROM usersidentifies the table.WHEREintroduces the condition that selects rows.- Single quotes surround a text value, called a string literal.
ANDrequires both comparisons to be true for the same row.- The final semicolon terminates the statement.
Alice's row passes both comparisons. The database returns (7, alice, customer), and the application creates Alice's session. With a wrong password, no row matches and this handler rejects the login. Figure 9.6 follows the normal input through this deliberately unsafe design.
Figure 9.6: The deliberately unsafe login snapshot compares both submitted values in SQL. The later parameterized example replaces this design and uses password-hash verification.
Text description of Figure 9.6
Alice enters the synthetic username alice and password AliceExample!42. The browser sends them to the login endpoint. The application constructs a query that compares the username and password with AND. The database returns Alice's matching row, and the application creates her session. The example stores readable passwords, making the comparison visible while also illustrating a password-storage failure.
Step 3: identify how malicious input changes the instruction
The attacker submits this username, including the quote, space, two hyphens, and trailing space:
admin' --
The submitted password is wrong. A payload is the input crafted to exercise a vulnerability. After the same concatenation, the database receives this single-line statement:
SELECT id, username, role FROM users WHERE username = 'admin' -- ' AND password = 'wrong';
The first quote came from the application. The quote after admin came from the payload and closes the username value. The -- starts a line comment, text the SQL interpreter ignores through the end of the line. The application's remaining quote and password comparison are now inside that comment. The effective row-selection condition is just:
WHERE username = 'admin'
The database returns the admin row even though the supplied password is wrong. The vulnerable handler treats the returned row as proof of a successful login and creates a session for user 1. No password was recovered or guessed. The application allowed input to remove the password test. Figure 9.7 marks the transition from a text value into SQL syntax.
Figure 9.7: The submitted quote closes the username value, and the comment disables the password condition. The diagram separates predicate components for annotation. The complete query in the prose remains one line.
Text description of Figure 9.7
The submitted username contains admin, a quote, and the SQL comment marker. Concatenation turns the quote into a string terminator. The comment makes the original password comparison inactive on the same generated query line. The database selects the admin row even though the supplied password is wrong, and the handler creates a session. This demonstrates login bypass without establishing database extraction.
These examples use SQL syntax supported by SQLite. MySQL requires whitespace or a control character after the two hyphens, so the trailing space is deliberate. Actual payload behavior depends on the database, quoting context, driver, and generated query. A newline inserted before the password condition would change this particular comment's reach. See SQLite comment syntax, MySQL comment syntax, and PortSwigger's SQL injection overview.
Another common teaching payload is ' OR 1=1 --. In this query shape, it creates a condition that can match every row because 1=1 is always true. A handler that fetches the first result can then accept an unintended account, but SQL does not guarantee an order without an ordering clause. Do not assume that the first account will be admin. A successful login response also does not mean the browser received all of the returned database rows. The application decides what it displays.
Step 4: distinguish login bypass from database disclosure
A database dump is an extracted copy of database data. Login bypass and data extraction are different outcomes. A login handler may return only a session cookie, even when its query matches many records. To understand disclosure, consider a separate example with hashed passwords and a vulnerable product-search feature.
This snapshot's products table has text columns name, description, and category. Its users table includes username and password_hash. A password hash is a stored result of a password-hashing function used for verification. It should not be a readable copy of the password. The search endpoint constructs:
SELECT name, description FROM products WHERE category = 'supplies';
The page renders both returned columns as product results. The attacker supplies the following category:
' UNION SELECT username, password_hash FROM users --
The resulting query is:
SELECT name, description FROM products WHERE category = '' UNION SELECT username, password_hash FROM users -- ';
UNION combines compatible result sets. In this example, the first query returns no products for the empty category. The injected query returns the usernames and stored hashes. Both sides return two text columns, so the application receives rows that fit its expected shape. The search page may display this synthetic result under its product headings:
name description
admin [ADMIN_HASH_OMITTED]
alice [ALICE_HASH_OMITTED]
The bracketed values are explanatory placeholders, not working hashes. Figure 9.8 shows how the second result set enters the existing output path.
Figure 9.8: UNION places compatible user-table values into the product search result. The diagram shows one illustrative row from the synthetic result set.
Text description of Figure 9.8
The original product query returns no rows for an empty category. The injected selection supplies username and password_hash as two compatible text columns. UNION combines the result sets, and the application displays the values under its existing name and description headings. The visible response path and database read permissions make disclosure possible. The hash label represents synthetic data.
Video: defending against SQL injection
Parameterized queries and a safer login design
A parameterized query sends the SQL structure separately from the data values. The database driver binds values into specified positions without interpreting their contents as new SQL syntax. For a hashed-password design using Python and SQLite:
# Teaching fragment. verify_password represents a vetted hash-verification API.
row = connection.execute(
"SELECT id, password_hash FROM users WHERE username = ?",
(username,),
).fetchone()
if row is not None and verify_password(password, row[1]):
create_session(user_id=row[0])
else:
reject_login()
The ? is a parameter placeholder. (username,) supplies one value. If the input is admin' --, the driver treats that entire sequence as a username to look up. The password check remains a separate call to a trusted password-verification function. That function compares the supplied password with the stored salted hash using the configured password-hashing algorithm.
The profile-update fix uses the same separation:
connection.execute(
"UPDATE users SET display_name = ? WHERE id = ?",
(display_name, session_user_id),
)
Here the malicious display-name text becomes a display-name value. It cannot add a role assignment. Validate allowed display-name content as a separate usability and data-quality rule. Restrict the database account's privileges, keep administrative operations separate, and use server-approved mappings for SQL elements that cannot be supplied as value parameters, such as a selectable sort-column name. See the OWASP SQL Injection Prevention Cheat Sheet.
Command injection: a network diagnostic page
Operating-system command injection lets input change a command the server executes. A shell is a command interpreter, such as /bin/sh, that understands command names, arguments, separators, and redirection. A web application may accidentally expose that interpreter when it constructs a shell command from a form field.
Consider an internal help-desk portal with a connectivity test. An authenticated employee enters an approved device's Internet Protocol (IP) address. The server runs one Linux ping probe and returns its output. A developer implements it with this vulnerable Python fragment:
# Deliberately vulnerable Linux teaching fragment.
result = subprocess.run(
"ping -c 1 " + host,
shell=True,
capture_output=True,
text=True,
timeout=5,
)
return result.stdout + result.stderr
host is the submitted field. shell=True asks Python to pass the constructed string through a shell. capture_output collects normal output and error output, and this handler returns both. Returning the text makes the injected command's result visible in the browser. The five-second timeout limits this teaching example's duration but does not remove its injection flaw.
Normal request and constructed command
The normal request uses the loopback address 127.0.0.1, so the portal checks its own server:
POST /diagnostics/ping HTTP/1.1
Host: portal.northstar.test
Content-Type: application/x-www-form-urlencoded
host=127.0.0.1
ping -c 1 127.0.0.1
The shell runs one command with the supplied address as an argument. The intended response is the ping output.
Injecting a second command
The attacker changes the form field to:
127.0.0.1; id
After decoding the form data, the application passes this to the shell:
ping -c 1 127.0.0.1; id
The semicolon separates two commands. The shell runs id after ping finishes, regardless of whether ping succeeded. id reports the process's user and group identity. An illustrative response after the ping output might be:
uid=33(www-data) gid=33(www-data) groups=33(www-data)
The identity and numeric values depend on the image and operating system. The relevant evidence is that a second command executed under the service account. An authenticated employee triggered server-side command execution because the diagnostic feature accepted shell syntax.
Other shell operators have different meanings. && runs the following command only after success, while | passes one command's output to another command's input. Their behavior also depends on the shell. A tester should explain the specific parsing context instead of treating all separators as interchangeable. See PortSwigger's command injection guide.
Reading /etc/passwd
The next input asks the shell to read the account file:
127.0.0.1; cat /etc/passwd
cat writes a file's contents to standard output, the stream this handler returns. A synthetic excerpt might appear after the ping output:
root:x:0:0:root:/root:/bin/bash
www-data:x:33:33:web service:/var/www:/usr/sbin/nologin
alex:x:1000:1000:Alex Rivera:/home/alex:/bin/bash
Each line describes a local account. Its seven fields are username, password marker, user identifier (UID), primary group identifier (GID), descriptive information, home directory, and login shell. The x marker indicates separately stored password information. These lines reveal account structure, including service accounts, but do not reveal the users' plaintext passwords.
Path traversal and command injection can both expose this file through different mechanisms. In traversal, the application opens an unintended path. Here the shell executes cat, and the application returns the command's output.
Reading /etc/shadow: show the permission boundary
The next payload targets the protected password-information file:
127.0.0.1; cat /etc/shadow
Under a normal unprivileged web-service account with appropriate file permissions, the illustrative result is:
cat: /etc/shadow: Permission denied
Command injection does not automatically turn the process into the root administrator. The operating system still evaluates the process's identity and permissions. Figure 9.9 shows the readable account file and the normally protected password file.
Figure 9.9: The shell runs the appended command with the web process's permissions. A separate shadow-file request succeeds only when that process has sufficient access.
Text description of Figure 9.9
The diagnostic field contains a loopback address followed by a semicolon and cat /etc/passwd. A vulnerable handler passes the combined command to a shell, which runs the commands in sequence. The service can usually read passwd. Testing shadow requires a separate payload naming that file and normally produces permission denied. Root execution or overly broad file permissions could make password-hash data readable.
Cross-Site Scripting: identifying which browser executes the input
Cross-Site Scripting (XSS) occurs when attacker-controlled content becomes executable code in a page viewed by another user. Hypertext Markup Language (HTML) describes a page's structure, and JavaScript supplies behavior. The browser creates a Document Object Model (DOM), an in-memory representation of the page that JavaScript can read and change.
An injected script runs within the affected page's origin, defined by its scheme, hostname, and port. It can interact with the page and make requests available to that origin under the victim's browser session. Its reach depends on the victim's privileges and browser controls. A session cookie marked HttpOnly cannot be read directly by JavaScript, but the browser may still attach it to requests initiated by the injected script. XSS can therefore act through a session without reading its cookie.
The examples below use alert to display a visible execution marker. A dialog demonstrates that the browser interpreted an injected instruction. It does not by itself demonstrate stolen data or account takeover. Realistic consequences include reading displayed customer information, changing page content to mislead users, and performing actions that the victim is authorized to perform. See PortSwigger's XSS overview.
Reflected XSS: input returns in the immediate response
The portal's help search accepts a q parameter. In the vulnerable version, the server builds a results heading by inserting that parameter directly into HTML:
# Deliberately vulnerable response construction.
html = "<h2>Results for: " + q + "</h2>"
For q=shipping, the response contains ordinary heading text. An attacker instead prepares a link whose decoded q value is:
<script>alert('CFS275 reflected')</script>
The encoded request path is:
/search?q=%3Cscript%3Ealert%28%27CFS275%20reflected%27%29%3C%2Fscript%3E
After a victim follows the link, the server returns this unsafe HTML:
<h2>Results for: <script>alert('CFS275 reflected')</script></h2>
This example assumes no policy blocks inline scripts. The browser interprets the script element and displays the alert. The input traveled from a crafted link to the server and then back to the victim's browser in one request/response sequence. It did not need to be stored in the application's database. Figure 9.10 traces that sequence.
Figure 9.10: Reflected XSS returns attacker-controlled markup in the immediate response to a victim's crafted request.
Text description of Figure 9.10
The attacker supplies a crafted search link. The victim follows it, sending the payload in the search request. The portal places the value into an HTML response without appropriate encoding. The victim's browser interprets that response and executes the injected script within the portal origin. The illustrated sequence does not require persistent storage.
The attacker generally needs the victim to make the crafted request, often by following a link or submitting a form. Seeing the same text printed literally on the page would not establish XSS. The browser must interpret it in an executable context. See PortSwigger's reflected XSS guide.
Video: defending against reflected XSS
Stored XSS: the application saves the payload for later viewers
The portal also accepts support-ticket comments. A customer submits a comment, the application saves it, and a support employee later opens the ticket. In a vulnerable version, the ticket page inserts the saved comment into HTML without safe handling.
The comment contains:
<img src="/missing-image" onerror="alert('CFS275 stored')">
The portal returns a non-image response for /missing-image, causing the browser's image load to fail. The onerror attribute is an event handler, JavaScript the browser runs when that event occurs. This example assumes no policy blocks inline handlers. When the saved comment becomes HTML in the support employee's page, the handler displays the marker.
The sequence has a time gap: submission, storage, later retrieval, then browser interpretation. Figure 9.11 shows that the employee can trigger the payload by visiting an ordinary ticket page.
Figure 9.11: Stored XSS persists between submission and a later visit. The unsafe rendering step exposes the saved comment to the staff browser.
Text description of Figure 9.11
A submitted comment carries a payload. The application stores it as data. Later, a staff member opens the ticket, and the application inserts the saved comment into HTML without appropriate output handling. The staff browser interprets the resulting content and displays the injected alert. The database stores the payload but does not execute the JavaScript.
Video: defending against stored XSS
9.7 A06:2025 Insecure Design
Insecure design is a flaw in how the application was planned rather than a mistake in how a specific line was coded. The feature works as designed, but the design itself failed to account for how it could be abused. No amount of clean coding fixes a design that never included a needed control. This category asks whether the application was built with threats in mind from the start.
How it is exploited
Insecure design usually shows up as business logic abuse, where an attacker uses legitimate features in an unintended sequence or at an unintended scale. Examples include:
- A password reset flow that lets an attacker enumerate valid accounts or reset another user's password because the design never verified the requester.
- A checkout process that applies a discount code repeatedly, or accepts a negative quantity that credits the account, because the design assumed cooperative input.
- A support workflow with no limit on attempts, letting an attacker guess a security answer or a one-time code endlessly.
Because the flaw is in the logic, automated scanners rarely find it. Discovery depends on understanding what the application is supposed to do and then asking how each step could be misused.
Tools most commonly used
Testing insecure design is mostly manual:
- Burp Suite: Intercepts, modifies, and replays workflow steps out of order or with unexpected values.
- Turbo Intruder: A Burp extension that helps test whether a step can be repeated faster than the design anticipated.
- OWASP Threat Dragon: Helps teams diagram a system and reason about abuse cases before code exists.
- Abuse cases: Describe how a feature could be turned against its purpose; they are the security-focused mirror image of use cases.
The developer-side fix
Security has to be part of design, through threat modeling, defined abuse cases, and reference to a standard such as the OWASP Application Security Verification Standard. Critical workflows should enforce limits, validate state transitions, and assume input will be hostile. The OWASP Cheat Sheet Series offers design-level guidance for common features such as authentication and password reset.
9.8 A07:2025 Authentication Failures
Authentication is how an application confirms that users are who they claim to be. Authentication failures (called identification and authentication failures in 2021) cover weaknesses that let an attacker defeat or bypass that confirmation, leading to account takeover. This category overlaps with the credential attacks in Chapter 8, applied here to the web login surface.
How it is exploited
- Credential brute forcing submits many username and password combinations against a login that lacks rate limiting or lockout.
- Password spraying tries a few common passwords across many accounts, which stays under per-account lockout thresholds. This often succeeds where brute force fails against modern policies.
- Credential stuffing reuses username and password pairs stolen in previous breaches, which works because people reuse passwords across sites.
- Weak session management, such as predictable session identifiers, tokens that are not invalidated at logout, or tokens exposed in URLs, lets an attacker take over a session.
- JSON Web Token (JWT) weaknesses, where a token that carries the user's identity can be forged. Common issues include accepting the
nonealgorithm (no signature), a weak signing secret that can be cracked, and algorithm confusion that tricks the server into verifying with the wrong key. - Weak or bypassable multifactor authentication (MFA), including flows that can be skipped or fatigued.
Video: securing JSON Web Tokens
Tools most commonly used
- Hydra and Medusa: Perform online password attacks against login services.
- Burp Intruder and ffuf: Carry out login brute forcing and password spraying against web forms with fine control over requests.
- jwt_tool: Analyzes and tests JSON Web Tokens for the
nonealgorithm, weak secrets, and algorithm confusion. - hashcat and John the Ripper: Crack tokens offline when they use weak signing secrets.
- CeWL: Builds a target-specific wordlist from the organization's own website to improve password-guessing tests.
- Patator: Provides a flexible alternative for online guessing across many services.
Warning
Online password attacks can lock out real user accounts and disrupt a production service. The rules of engagement should specify test accounts, request rates, lockout behavior, and stop conditions. Stop and report unexpected disruption to legitimate users.
The developer-side fix
Applications should support multifactor authentication, enforce rate limiting and sensible lockout, and store passwords with a strong algorithm as covered under cryptographic failures. Session identifiers must be random, transmitted securely, and invalidated on logout. JSON Web Tokens should use a strong signing key and a fixed, verified algorithm, and the server should reject the none algorithm outright.
9.9 A08:2025 Software or Data Integrity Failures
Integrity means that code and data have not been changed by someone unauthorized. Software or data integrity failures occur when an application trusts code, updates, or data without verifying that they are genuine and unmodified. If the application accepts something on faith that an attacker can tamper with, the attacker can substitute their own content.
How it is exploited
- Insecure deserialization. Serialization turns an object into a stream of bytes for storage or transmission, and deserialization rebuilds it. If an application deserializes data supplied by a user without validation, an attacker can craft a malicious serialized object that runs code as it is rebuilt. This uses gadget chains, sequences of existing library classes that combine to produce code execution during deserialization.
- Unverified updates. An application that downloads and applies an update without checking a signature can be fed a malicious update by an attacker positioned in the path.
- Untrusted data in a trusted structure. An application that trusts data from a source it should have verified, such as a signed cookie whose signature is not actually checked, lets an attacker modify that data.
- Insecure CI/CD. A build or deployment pipeline that applies changes without verifying their origin can push attacker-controlled code into production. This overlaps with the supply chain concerns in A03.
Tools most commonly used
- ysoserial and ysoserial.net: Generate serialized Java and .NET test objects using known gadget chains.
- phpggc: Generates deserialization test payloads for PHP applications.
- Java Deserialization Scanner and Freddy: Burp Suite extensions that detect and help test deserialization endpoints.
- GadgetProbe: Helps identify which library classes are present on the server so a tester can choose an appropriate gadget chain.
The developer-side fix
Applications should not deserialize untrusted data. Where objects must be exchanged, a data-only format with strict validation is safer than deserializing arbitrary objects. Updates and critical data should carry digital signatures that the application verifies before use. CI/CD systems should verify the integrity and origin of code and dependencies before building or deploying.
9.10 A09:2025 Security Logging and Alerting Failures
Security logging and alerting failures are the gaps in an application's ability to record security-relevant events and respond to them. The name changed in 2025 from "monitoring" to "alerting" to emphasize that recording an event has value only if it triggers a response. An application that logs nothing, or logs everything but alerts on nothing, cannot detect an attack in progress or reconstruct one afterward.
Why a pentester cares
This category is defensive, so it is tested differently from the others. A tester rarely exploits missing logging directly. Instead, the tester evaluates whether their own activity would have been detected, which is valuable evidence for the client. Findings in this category require comparison with the defensive records: whether failed logins were logged, whether defined alert conditions triggered notifications, and whether responders received them. A tester cannot infer the absence of server-side logging from an ordinary browser response. On a purple team engagement, where offense and defense work together, confirming which attacker actions the defenders saw is a primary goal.
How the gap shows up
- Login failures, access control denials, and input validation failures are not logged.
- Logs exist but contain no useful detail, or store sensitive data such as passwords in cleartext, which creates a separate exposure.
- No alerting is configured, so even recorded events reach no one in time to act.
- Logs can be altered or cleared by the same account an attacker compromises, destroying the evidence.
Video: logging and alerting failures
Tools most commonly used
Because this is about detection and response, testing leans on the tools already in use plus observation:
- Burp Suite and custom scripts: Generate the suspicious activity a defender should catch.
- Atomic Red Team and MITRE Caldera: Run known attacker techniques and check which ones the defense records and alerts on.
- NIST Guide to Computer Security Log Management (SP 800-92): Provides recognized guidance for logging on the defensive side.
The developer-side fix
Applications should log authentication events, access control failures, and input validation failures with enough context to investigate, without recording secrets. Logs should be protected from tampering and sent to a central system, and meaningful events should generate alerts that a responder actually receives.
9.11 A10:2025 Mishandling of Exceptional Conditions
Mishandling of exceptional conditions is new in 2025. It covers what an application does when something unexpected happens: an error, a malformed input, a resource that is unavailable, or an operation that fails partway through. Software spends most of its design effort on the path where everything goes right. Attackers focus on the paths where things go wrong, because those paths are less tested and often handled insecurely.
How it is exploited
- Fail-open behavior. A security check that errors out and then allows the action instead of denying it. If an authorization service is unreachable and the application responds by granting access, an attacker who can cause that error gains access.
- Information disclosure through errors. An unhandled error returns a stack trace, an internal path, or a database message that helps the attacker map the system. This overlaps with security misconfiguration when it is a matter of settings.
- Inconsistent state after partial failure. An operation that fails halfway and leaves the application in a state the logic did not anticipate, such as a funds transfer that debits one account without crediting the other.
- Race conditions. Two requests sent at nearly the same moment both pass a check that assumed only one would, for example redeeming a single-use coupon or withdrawal twice before the balance updates.
The testing approach is to deliberately break things: send unexpected data types, oversized inputs, missing fields, and rapid concurrent requests, then watch whether the application fails safely or fails open.
Tools most commonly used
Fuzzers drive this category:
- ffuf, wfuzz, and Burp Intruder: Send malformed and unexpected inputs to see how the application reacts.
- Turbo Intruder: A Burp extension that sends many requests with precise timing to test for race conditions.
- Python scripts: Reproduce specific failure sequences and concurrent requests that off-the-shelf tools do not cover.
The developer-side fix
Applications should handle errors explicitly and fail closed, denying the action when a security-relevant operation cannot complete. Error messages shown to users should be generic, with detail kept in protected server-side logs. Operations that must be atomic should be implemented so that a partial failure does not leave an exploitable state, and shared resources should be protected against concurrent access.
9.12 The Web Application Pentester's Toolkit
Several tools appear across many categories because they operate at the level of the HTTP request itself. The two that anchor almost every web assessment are the intercepting proxies. Learn one of them well before specializing.
- Burp Suite is the industry-standard intercepting proxy. It sits between the browser and the application, letting the tester read, modify, and replay every request. Its Repeater, Intruder, and extension ecosystem support work across every category in this chapter.
- ZAP is a free, open-source web application security testing tool with an intercepting proxy and a similar request-inspection workflow.
The table below maps common tools to the categories where they are used most.
| Category | Representative tools |
|---|---|
| A01 Broken Access Control | Burp Suite (Repeater, Intruder), Autorize, ZAP, ffuf, gobuster, Postman, curl, interactsh (SSRF) |
| A02 Security Misconfiguration | Nikto, Nmap scripting engine, nuclei, gobuster, ffuf, provider CLIs, IAM Access Analyzer, cloud_enum, curl |
| A03 Software Supply Chain Failures | OWASP Dependency-Check, Dependency-Track, retire.js, Trivy, Grype, Syft, OSV-Scanner, searchsploit |
| A04 Cryptographic Failures | testssl.sh, sslscan, sslyze, Nmap ssl-enum-ciphers, Wireshark, hashcat, John the Ripper, CyberChef |
| A05 Injection | sqlmap, commix, dalfox, XSStrike, tplmap, Burp Suite, ffuf, wfuzz |
| A06 Insecure Design | Burp Suite (manual testing), Turbo Intruder, OWASP Threat Dragon |
| A07 Authentication Failures | Hydra, Medusa, Burp Intruder, ffuf, jwt_tool, hashcat, CeWL, Patator |
| A08 Software or Data Integrity Failures | ysoserial, ysoserial.net, phpggc, Burp (Java Deserialization Scanner, Freddy, GadgetProbe) |
| A09 Security Logging and Alerting Failures | Burp Suite, Atomic Red Team, MITRE Caldera (detection validation) |
| A10 Mishandling of Exceptional Conditions | ffuf, wfuzz, Burp Intruder, Turbo Intruder, custom Python scripts |
Reference Table: OWASP Top 10 2025 at a Glance
| ID | Category | What it is | Primary defense |
|---|---|---|---|
| A01 | Broken Access Control | Missing or broken checks on who can do or reach what (includes IDOR, path traversal, SSRF) | Enforce server-side, deny by default, check object ownership |
| A02 | Security Misconfiguration | Insecure settings anywhere in the stack | Hardened baseline, no defaults, minimal features |
| A03 | Software Supply Chain Failures | Risk from trusted third-party code, builds, and dependencies | Inventory and patch components, verify sources, use an SBOM |
| A04 | Cryptographic Failures | Sensitive data unprotected or weakly protected | Current TLS, strong password hashing, managed secrets |
| A05 | Injection | Untrusted input changes a command or query (SQLi, command, XSS, SSTI) | Parameterized queries, safe APIs, output encoding |
| A06 | Insecure Design | The design itself omitted a needed control | Threat modeling, abuse cases, secure design standards |
| A07 | Authentication Failures | Attackers defeat or bypass identity checks | MFA, rate limiting, strong session and token handling |
| A08 | Software or Data Integrity Failures | Trusting code or data without verifying it | No untrusted deserialization, verify signatures, secure CI/CD |
| A09 | Security Logging and Alerting Failures | Events not recorded, or recorded but not acted on | Log security events, protect logs, alert responders |
| A10 | Mishandling of Exceptional Conditions | Insecure behavior on errors and unexpected states | Fail closed, generic errors, atomic operations |
Chapter Summary
- The OWASP Top 10 is a vendor-neutral awareness document that ranks the ten most significant categories of web application risk. It is built from contributed data (eight categories) and a community survey (two categories), and it gives testers, developers, and managers a shared vocabulary for findings.
- The 2025 edition keeps Broken Access Control at the top, raises Security Misconfiguration to second, expands the components category into Software Supply Chain Failures, and adds a new category, Mishandling of Exceptional Conditions. SSRF moved under Broken Access Control, while XSS remains under Injection.
- Broken Access Control (A01) and Injection (A05) remain the categories a web tester exploits most directly, using intercepting proxies such as Burp Suite and ZAP along with targeted tools like sqlmap and Autorize.
- Security Misconfiguration (A02), Supply Chain Failures (A03), and Cryptographic Failures (A04) are found more through discovery and inventory than through a single exploit, using scanners, dependency analyzers, and TLS inspection tools.
- Insecure Design (A06) and Mishandling of Exceptional Conditions (A10) reward manual testing and abuse-case thinking, because scanners rarely find logic and failure-path flaws.
- Authentication Failures (A07) and Software or Data Integrity Failures (A08) connect this chapter to the credential attacks in Chapter 8 and to the supply chain and cloud material in Chapter 10.
- Security Logging and Alerting Failures (A09) is assessed by observing whether your own activity would have been detected, which is central to purple team work.
- API replay compares identity, object, operation, and response content. Postman and curl make requests repeatable, but the server must enforce ownership and action permissions.
- Path traversal changes the file an application accesses. SSRF changes where the server sends a request. Each crosses a different resource boundary.
- Public storage testing separates object reading, object listing, and writing. Confirm ownership, intended access, and whether a request carried credentials or a signed URL.
- SQL injection changes database instructions. A login bypass, a UNION-based disclosure, and an injected UPDATE require different query and output conditions. Parameter binding separates data values from SQL syntax.
- Command injection exposes a command interpreter through application input. File access still depends on the service account's permissions. An account list and password-hash disclosure are different findings.
- Reflected and stored XSS follow different delivery and processing paths, but both require unsafe browser interpretation. Use output controls appropriate to the actual HTML context.