5

I would like to implement Csrf protection with NestJS and Quasar. But I think I misunderstand something... btw I'm not doing SSR, so I don't send the form from the back to the view.

Here is the NestJs back-end code:

    async function bootstrap() {
  const PORT = process.env.PORT;
  const app = await NestFactory.create(AppModule, {
    cors: true,
    bodyParser: false,
  });
  console.log(`your App is listening on port ${PORT}`);

  // Added Cookie-parser to user csurf packages
  // Prevent CSRF attack
  app.use(cookieParser());
  app.use(csurf({ cookie: true }));

  await app.listen(PORT);
}
bootstrap();

So I'm just using CookieParser and csurf package. On my login page I call a "csrf endpoint" just to send a cookie to the view, to send it back with the post call (login). I still get the "invalid csrf token" AND a CORS error and don't know why....(see screen below), any suggestions to make it works ?

csrf cookie

When I try to login, error in the browser: CORS error

Error 500 login

And error in the back-end:

nestjs csrf errror

Same error if I try a request with insomnia. I thought that the CSRF token is attached to the "web browser" to go back to the back-end with nest request, so why I'm still getting this error ? Insomnia send the cookie automatically with the right request so the token should go back to the back-end. Any idea ? Regards

EDIT: After many times reading docs, It seems that CSRF protection is for SSR only ? No need to add csrf security with SPA ? Could anyone can confirm ?

EDIT: Here's another work:

The purpose here is to send a request before login to get a csrf token that I can put into a cookie to resend when I login with a POST method.

Here is my endpoint:

import { Controller, Get, Req, Res, HttpCode, Query } from "@nestjs/common";

@Controller("csrf")
export class SecurityController {
  @Get("")
  @HttpCode(200)
  async getNewToken(@Req() req, @Res() res) {
    const csrfToken = req.csrfToken();

    res.send({ csrfToken });
  }
}

Here is what I've done into my main.ts file (I'll explain below):

async function bootstrap() {
  const PORT = process.env.PORT;
  const app = await NestFactory.create(AppModule, {
    cors: {
      origin: "*",
      methods: ["GET,HEAD,OPTIONS,POST,PUT"],
      allowedHeaders: [
        "Content-Type",
        "X-CSRF-TOKEN",
        "access-control-allow-methods",
        "Access-Control-Allow-Origin",
        "access-control-allow-credentials",
        "access-control-allow-headers",
      ],
      credentials: true,
    },
    bodyParser: false,
  });
  app.use(cookieParser());
  app.use(csurf({ cookie: true }));
  console.log(`your App is listening on port ${PORT}`);

  await app.listen(PORT);
}
bootstrap();

And here my axiosInstance Interceptors of the request in my VueJS frontend:

axiosInstance.interceptors.request.use(
(req) => {
  const token = Cookies.get('my_cookie')
  if (token) {
    req.headers.common['Authorization'] = 'Bearer ' + token.access_token
  }

  req.headers['Access-Control-Allow-Origin'] = '*'
  req.headers['Access-Control-Allow-Credentials'] = 'true'
  req.headers['Access-Control-Allow-Methods'] = 'GET,HEAD,OPTIONS,POST,PUT'
  req.headers['Access-Control-Allow-Headers'] =
    'access-control-allow-credentials,access-control-allow-headers,access-control-allow-methods,access-control-allow-origin,content-type,x-csrf-token'

  const csrfToken = Cookies.get('X-CSRF-TOKEN')
  if (csrfToken) {
    req.headers['X-CSRF-TOKEN'] = csrfToken
    console.log(req)
  }

  return req
},
(err) => {
  console.log(err)
},

Here the same for repsonse:

axiosInstance.interceptors.response.use(
(response) => {
  if (response?.data?.csrfToken) {
    const {
      data: { csrfToken },
    } = response
    Cookies.set('X-CSRF-TOKEN', csrfToken)
  }

  return response
},

And inside my login I make a call on the mounted function of my login component:

 async mounted() {
const result = await securityService.getCsrf()

},

So now to explain:

As I said I'm not building a SSR project, that's why I want to send the token into a classic axios reponse and store it in a Cookie (this part is for test I heard that storing a csrf token into a classic cookie is not the right way.) And for each next request I get the csrf token and "attach" it to the request into the headers, making my headers "custom". Here is a problem I don't know how to make custom headers works with nestJS and CORS, that's why I try many thing with CORS options in NestJS and writte some custome header before the request go to the back-end but without success, I've got the same error message:

another CORS problem

I'm a bit confuse about this problem and CORS/CSRF is a big deal for spa, my questions still the same, with CORS and SameSite cookie attributes, and my api is in a subdomain of my front-end, is it really necessary to make a anti-csrf pattern ?

Btw how can I make my custom headers working and why CORS say to me there is no "Access-Control-Allow-Origin" header but there is:

Request headers

De_Jr
  • 191
  • 3
  • 11

1 Answers1

0

try to generate csrf token and pass to front on each petition

// main.ts - from NestJs - Backend
// after app.use(csurf({ cookie: true }))

app.use((req: any, res: any, next: any) => {
  const token = req.csrfToken()
  res.cookie("XSRF-TOKEN", token)
  res.locals.csrfToken = token
  next()
})

from: https://github.com/nestjs/nest/issues/6552#issuecomment-1175270849

CharlyDan
  • 1
  • 1