BREAKING: CVE-2026-18165 - @fastify/oauth2 Login CSRF via Plantable State Cookies

BREAKING: CVE-2026-18165 - @fastify/oauth2 Login CSRF via Plantable State Cookies

@fastify/oauth2 7.2.0 through 8.2.0 accepts plantable OAuth state cookies from related hosts, enabling login CSRF. Upgrade to 8.3.0 and enable hostPrefixedCookies.

2 min read351 words
Contents

TL;DR: @fastify/oauth2 versions 7.2.0 through 8.2.0 can accept attacker-planted OAuth state cookies from a related host. The callback only checks that query values match cookies, not that the victim browser initiated the flow. An attacker who can set cookies for the application host can complete an attacker-owned OAuth flow inside a victim's browser and silently sign the victim in to the attacker's account. Upgrade to 8.3.0 and enable hostPrefixedCookies: true. Upgrading without enabling that option does not change the vulnerable cookie behavior.

What happened

The plugin stores OAuth state in the unprefixed oauth2-redirect-state cookie. With PKCE enabled, it also stores the verifier in oauth2-code-verifier. A sibling or related host that can write cookies for the application's host can plant matching state and verifier values before the victim reaches the callback.

The callback compares the query parameters with those cookies. That proves the values match, but it does not prove the same browser started the authorization flow. The attacker can therefore finish their own OAuth authorization inside the victim's browser. PKCE does not prevent the attack because the verifier is read from a second cookie that can be planted the same way.

The result is login CSRF. The victim is signed in to the attacker's account. The advisory explicitly notes that this does not expose the victim's own account, credentials, session, or OAuth tokens.

What to do

Upgrade the package:

npm install @fastify/oauth2@^8.3.0

Then enable host-prefixed cookies. The mitigation is opt-in because it requires HTTPS:

fastify.register(oauthPlugin, {
  /* ... */
  hostPrefixedCookies: true
})

If you must remain on 7.2.0 through 8.2.0, use __Host- prefixed state and verifier cookie names with secure: true and path: '/'. The strongest alternative is to bind state to a server-side browser session with generateStateFunction and checkStateFunction.

Affected scope and severity

  • Affected: @fastify/oauth2 >= 7.2.0, < 8.3.0
  • Fixed: 8.3.0, with hostPrefixedCookies: true enabled
  • CVSS 3.1: 4.2 MEDIUM
  • Vector: CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:L/A:N
  • CWE: CWE-352, Cross-Site Request Forgery

References and full details

Continue reading

All posts