Morgbn / nuxt-csurf

Nuxt Cross-Site Request Forgery (CSRF) Prevention
https://nuxt-csurf.vercel.app
MIT License
67 stars 15 forks source link

Csrf token mismatch, Nuxt v3.1.0 #2

Closed hopkins385 closed 1 year ago

hopkins385 commented 1 year ago

Hi guys,

first of all thank you very much for the package! Unfortunately it seems to not work. What I've observed so far:

The csrf-token remains "undefined" even if the cookie is set properly.

nuxt config

  csurf: {
    cookieKey: 'csrf',
  },

Request header

POST /api/userpage HTTP/1.1
Accept-Encoding: gzip, deflate, br
Accept-Language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7
Cache-Control: no-cache
Connection: keep-alive
Content-Length: 14
Cookie: csrf=1026e2f5-f643-4223-8920-9b4c16d2f1d0
Host: localhost:3000
Origin: http://localhost:3000
Pragma: no-cache
Referer: http://localhost:3000/signup
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36
accept: application/json
content-type: application/json
csrf-token: undefined
sec-ch-ua: "Not_A Brand";v="99", "Google Chrome";v="109", "Chromium";v="109"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "macOS"

Response header

HTTP/1.1 403 CSRF Token Mismatch
Access-Control-Allow-Origin: *
content-type: application/json
date: Wed, 25 Jan 2023 21:25:50 GMT
connection: close
content-length: 512

package json (relevant parts)

    "nuxt": "^3.1.0",
    "nuxt-csurf": "^1.0.0",

api call

    const { data, pending, error, refresh } = useCsrfFetch('/userpage', {
      baseURL: config.public.apiBase,
      method: 'POST',
      body: JSON.stringify({ slug }),
    });
hopkins385 commented 1 year ago

Ok, after trying out your playground example, I figured out that I have to call useCsrf(); in the vue components itself (on the client side), before I make the api call since the api call is inside of a composable.

But that does exposes the csrf-token and the csrf (__Host-csrf) cookie to the client. Is that intentionally?

Morgbn commented 1 year ago

Hello @hopkins385, yes I should have added the useCsrf() in the doc 👍

Yes, both need to be exposed, the token to be added in each request header and the cookie to verify the token on the server side. Thus, an attacker cannot forge a request, because he cannot know in advance the correct value of the token that matches the cookie

See OWASP CSRF cheatsheet 😉

github-actions[bot] commented 1 year ago

:tada: This issue has been resolved in version 1.0.1 :tada:

The release is available on:

Your semantic-release bot :package::rocket:

hopkins385 commented 1 year ago

Ok, cool. In the meantime I've created a global middleware to add the useCsrf() to every request, because I had issues when navigating between pages as the useCsrf defined in the vue-component would only be executed on a full page reload, but not on a route change e.g. via NuxtLink.

Hope this helps all others who encounter similar issues. ;-)

Maybe worth to mention in docu?

./middleware/csrf.global.ts

export default defineNuxtRouteMiddleware(() => {
  useCsrf();
});
Morgbn commented 1 year ago

Thanks for the interest, just for information, with the last release (1.1.0) it's no longer necessary to manually (or via middleware) call the useCsrf() function :)