Full Report
Back in the day, websites were truly static, with only HTML and CSS being returned. Over time, responsive web design became a thing with AJAX/XHR requests being made in the background to get the information for the page. The Single Page Application (SPA) is extremely common to see now-a-days and that uses this pattern. SPAs are snappy and react quickly. However, collecting data from the server takes time after all of the parsing things to be done. The frameworks for this are quite heavy as well. So, something new has gained popularity: HTML Over the Wire (FROW). FROW is an architecture that attempts to combine to pre-rendering of HTML and the quickness of SPAs. It tries to render the HTML for the initial page on the backend but contains the ability to alter the page in an SPA-style fashion for dynamic portions. Hotwire Turbo, Unpoly, HTMLX and many other libraries take this approach. Clicking links is kind of weird with this architecture. Should it reload the page like an old webpage or act like an SPA? Most of the libraries fire off a fetch() in the background then update the UI without reloading the page. This overwrites the default functionality of just making a GET request. In order to support this, the FROW libraries add custom HTML attributes in order to allow for changing and editing of the fetch() request being made. For instance, you can change the method being used, the headers and more. The functionality is only intercepted from the browser if the origin of the path is the same as an internal meta tag. To top this off, CSRF tokens are added into a header of the request automatically for most of these FROW apps. So, here's the idea: can we trick the application to send a user CSRF tokens on the request by poisoning the allowed domains? On Turbo Drive, the turbo-root is can be set in a meta tag. According to the author, they've seen cases where it's possible for control this location as an attacker. Since the application thinks this is a trusted link, it will send the CSRF token alongside it. On HTMX, the same thing can happen. If the link of the request can be set and the csrf token is supposed to be in the body, then it will blindly send it. Overall, an interesting post on the integration of technology causing weird issues. I'm not sure how exploitable this really is, since many things have to come together though.
Analysis Summary
# Vulnerability: CSRF Token Exposure in HTML Over the Wire (FROW) Frameworks via Configuration Poisoning
## CVE Details
- CVE ID: Not explicitly assigned in the text. (The research focuses on architectural flaws in multiple libraries.)
- CVSS Score: Not explicitly calculated. (Severity is implied as **Medium/High** due to potential token leakage.)
- CWE: CWE-613: Insufficient Logout or Session Timeout (Related to session token lifecycle) / CWE-388: Reliance on Client-Side Information (in relation to trusting client-set roots).
## Affected Systems
- Products: Hotwire Turbo, HTMX (Other FROW libraries may be susceptible to similar flaws).
- Versions: Specific vulnerable versions are not mentioned, but the issue exists in versions where `turbo-root` can be configured via a meta tag to an arbitrary external URL, and where CSRF token transmission rules are applied universally regardless of the cross-origin context defined by that root.
- Configurations:
* **Hotwire Turbo:** When the `turbo-root` meta tag is controlled or poisoned by an attacker to point to an external, attacker-controlled domain.
* **HTMX:** When an `hx-header` attribute setting the CSRF token header (e.g., `X-CSRF-Token`) is present on a parent element, and a subsequent link/form targets an attacker-controlled external domain.
## Vulnerability Description
Frameworks adopting the HTML Over the Wire (FROW) architecture (like Hotwire Turbo and HTMX) intercept standard link clicks/form submissions to handle navigation asynchronously (SPA-style) without full page reloads, often sending requests via `fetch()`. To maintain CSRF protection, these frameworks automatically inject CSRF tokens (usually via headers like `X-CSRF-Token`) for "local" requests defined by configuration.
The flaw arises from how the scope for these automatic CSRF token injections is defined:
1. **Hotwire Turbo:** The scope is defined by the `turbo-root` meta tag. If an attacker can control the value of this tag to point to an external domain, Turbo will treat requests matching this external root as "local" and automatically attach the user's sensitive CSRF token header to the cross-origin request directed at the attacker's server.
2. **HTMX:** If an `hx-header` attribute containing the CSRF token instruction is set on a parent element, HTMX will blindly include that header on subsequent link clicks/form submissions, even if the destination URL is cross-origin and controlled by an attacker.
This mechanism leads to the disclosure of CSRF tokens across domain boundaries without proper same-origin policy checks related to the token injection mechanism.
## Exploitation
- Status: PoC available (The author demonstrated the successful transmission of the CSRF token to an attacker-controlled domain in testing environments for both frameworks).
- Complexity: Low to Medium (Requires injection of malicious HTML/attributes on a page where link navigation is intercepted by the FROW library).
- Attack Vector: Network (Requires the victim to navigate to a vulnerable application page, click a crafted link targeting an external site).
## Impact
- Confidentiality: High (Disclosure of short-lived CSRF tokens, allowing bypass of CSRF protections on subsequent requests made by the attacker).
- Integrity: High (If the attacker successfully uses the leaked CSRF token, they can forge state-changing requests on behalf of the victim user).
- Availability: Low (No direct impact on service availability).
## Remediation
### Patches
- **Hotwire Turbo:** No specific patch version mentioned in the text ("no patch has been released" at the time of writing). Developers must monitor Hotwire releases for updates addressing restrictions on `turbo-root` values.
- **HTMX:** No specific patch version mentioned in the text. Developers must monitor HTMX releases for updates that conditionally apply headers based on link destination safety/origin.
### Workarounds
1. **Input Validation:** Developers must strictly validate and sanitize the source of any configuration data used to set framework directives like `turbo-root` or any dynamically applied `hx-header` attributes to ensure they do not point to external domains or allow arbitrary input if set server-side.
2. **Header Policy Restriction:** Where possible, review framework configurations to ensure cross-origin requests do not automatically receive sensitive headers intended for same-origin API interactions.
3. **CSRF Token Placement:** If possible, configure applications to use cookie-based CSRF protection or embed the token in the request body (as opposed to headers), if the FROW library implementation for header transmission is the primary vector.
## Detection
- Indicators of Compromise: Outbound network traffic originating from the client browser directed towards attacker-controlled domains that include known CSRF token headers (e.g., headers starting with `X-CSRF-Token` or similar application-specific headers).
- Detection Methods and Tools: Web Application Firewalls (WAFs) or network monitoring tools capable of inspecting outbound HTTP requests from known application endpoints for suspicious cross-origin header injection, especially involving standard CSRF token header names.
## References
- Vendor Advisories: None available stated in the source text.
- Relevant links - defanged:
* `developer documentation link for turbo-root` (Implied location where the root is set)
* `developer documentation link for hx-header` (Implied location where headers are applied)