The Shifting Landscape of Web Application Audits

Traditional web application security audits often focus on server-side vulnerabilities. The assumption is a robust backend infrastructure that can be probed for weaknesses. However, a recent authorized audit of a small web store highlighted a critical shift in this paradigm. When the application is a Single Page Application (SPA) hosted on a platform like Vercel, with no discernible backend API to attack, the audit must pivot. The focus moves from server exploits to the assumptions baked into the client-side application itself and its business logic.

This audit targeted a small e-commerce site deployed on Vercel. The site offered standard e-commerce features: a product catalog, individual product pages, a shopping cart, and functionality for sharing cart contents via URLs. Initial reconnaissance suggested a Next.js application, a common framework for such deployments. The reality was a React + Vite SPA. This misreading of the technology stack initially influenced the audit approach, but the core mindset remained constant: shift from a defensive posture of "how do I protect this?" to an offensive one of "how can I break it?"

Diagram illustrating the typical flow of a client-side web application with a backend API.

Step 1: Reconnaissance - The Surface Level

The initial phase involved standard reconnaissance techniques. This included examining robots.txt and sitemap.xml for any exposed paths or administrative interfaces. The audit also mapped out the application's routes and parameters, carefully observing product and cart flows. HTTP headers were scrutinized for clues about the underlying technology or potential misconfigurations. Despite this thorough surface-level examination, zero findings emerged. There were no obvious hidden /admin panels, and the sitemap revealed nothing out of the ordinary. This lack of conventional entry points signaled that the vulnerabilities, if any, would lie deeper within the application's logic or its reliance on external integrations.

Step 2: The "Where's the Server?" Dilemma

The absence of a clear backend API was the central challenge. In a typical scenario, an auditor would expect endpoints like /api/products to fetch catalog data or /api/cart to manage the shopping cart. These were conspicuously absent. The application's functionality, such as displaying product details or adding items to the cart, appeared to be handled entirely client-side. This suggested that product data might be embedded within the JavaScript bundle, fetched from static files, or perhaps pulled from a headless CMS or a third-party service that wasn't directly exposed as a traditional API. The audit had to adapt, treating the SPA itself as the primary attack surface.

Step 3: Identifier Manipulation - Exploiting Business Logic

The most significant findings emerged from exploring identifier manipulation. The audit team focused on how the application identified and referenced its resources, particularly products and carts. For instance, the feature allowing users to share their carts via URLs presented a prime opportunity. Typically, a cart might be identified by a unique, opaque identifier (e.g., a UUID or a long, random string). If the application used predictable or sequential identifiers, or if it failed to properly validate ownership or existence of the referenced cart, an attacker could potentially view, modify, or even hijack another user's cart.

Consider a scenario where a shared cart URL looks like store.com/share/cart/12345. An auditor would test changing the `12345` to `12344` or `12346` to see if they could access other users' carts. If the application simply displayed the cart contents based on this ID without verifying that the current user had permission to view it, this would be a critical business logic vulnerability. Similarly, product identifiers on product pages could be manipulated. If product IDs were sequential (e.g., /product/1, /product/2), an attacker might try to access information about products that were not intended to be public, or even try to manipulate pricing or inventory if the client-side code made such requests insecurely.

Example of a predictable URL structure that could be vulnerable to identifier manipulation.

Step 4: Clickjacking - Hijacking User Interaction

The second major finding was clickjacking. This attack vector involves tricking a user into clicking on something different from what the user perceives. In the context of this SPA, clickjacking could be achieved by embedding the site's legitimate interface within an invisible iframe on a malicious website. When the user visits the malicious site, they might see a seemingly innocuous button or link. However, beneath it, the legitimate site's interface is layered. If the user clicks the button on the malicious site, they are inadvertently interacting with an element on the embedded legitimate site.

For example, a malicious site could present a fake "Claim your prize!" button. Beneath this, layered invisibly, is the "Add to Cart" button of the e-commerce store. If the user clicks to claim their prize, they might inadvertently add an expensive item to their cart on the legitimate store, or worse, approve a transaction if the site had such functionality exposed client-side without proper safeguards. Effective defenses against clickjacking include the use of the X-Frame-Options HTTP header set to DENY or SAMEORIGIN, or employing Content Security Policy (CSP) directives like frame-ancestors 'self'. The audit confirmed that these headers were either missing or misconfigured, leaving the application vulnerable.

The Broader Implications for Auditing

This audit serves as a crucial reminder that the attack surface for web applications is evolving. The rise of SPAs, serverless architectures, and Jamstack deployments means that traditional backend-focused security testing is insufficient. Auditors must be prepared to:

  • Deeply understand client-side logic: Analyze JavaScript code to identify how data is fetched, processed, and validated.
  • Focus on business logic flaws: Look for vulnerabilities in how the application handles identifiers, state, and user permissions, especially where direct user input or manipulation is possible.
  • Scrutinize third-party integrations: If data is pulled from external services, understand the security implications of those integrations.
  • Validate security headers: Ensure that headers like X-Frame-Options and CSP are correctly implemented to prevent common client-side attacks like clickjacking.

The assumption that every web application has a monolithic backend to attack is no longer valid. The real vulnerabilities often lie in the business logic and the assumptions developers make when building client-heavy applications. For developers building these modern web experiences, this means a heightened awareness of client-side security and rigorous testing of the application's core business rules, not just its API endpoints.