NestJS 的 csrf 令牌无效

发布于 2025-01-10 19:44:37 字数 4636 浏览 0 评论 0原文

我想用 NestJS 和 Quasar 实现 Csrf 保护。 但我想我误解了一些东西...... 顺便说一句,我不做 SSR,所以我不会将表单从后面发送到视图。

这是 NestJs 后端代码:

    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();

所以我只使用 CookieParser 和 csurf 包。 在我的登录页面上,我调用“csrf 端点”只是为了将 cookie 发送到视图,并通过 post 调用(登录)将其发送回来。 我仍然收到“无效的 csrf 令牌”和 CORS 错误,但不知道为什么......(请参见下面的屏幕),有什么建议可以让它发挥作用吗?

csrf cookie

当我尝试登录时,浏览器出现错误: CORS 错误

Error 500 login

后端错误:

nestjs csrf errror

相同如果我因失眠而尝试请求,则会出错。 我认为 CSRF 令牌附加到“网络浏览器”以通过嵌套请求返回后端,那么为什么我仍然收到此错误? Insomnia 会根据正确的请求自动发送 cookie,因此令牌应该返回到后端。 有什么想法吗? 问候

编辑: 多次阅读文档后,似乎CSRF保护仅适用于SSR?无需使用 SPA 添加 csrf 安全性?任何人都可以确认吗?

编辑:这是另一项工作:

这里的目的是在登录之前发送请求以获取 csrf 令牌,我可以将其放入 cookie 中以便在使用 POST 方法登录时重新发送。

这是我的端点:

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 });
  }
}

这是我在 main.ts 文件中所做的事情(我将在下面解释):

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();

这里是我的 VueJS 前端中请求的 axiosInstance 拦截器:

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)
},

这里的回复也是如此:

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

  return response
},

在我的登录名中我调用我的登录组件的已安装函数:

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

},

现在解释一下:

正如我所说,我不是在构建 SSR 项目,这就是为什么我想将令牌发送到经典的 axios 响应并将其存储在一个 Cookie(这部分是为了测试我听说将 csrf 令牌存储到经典 cookie 中不是正确的方法。) 对于下一个请求,我获取 csrf 令牌并将其“附加”到请求的标头中,使我的标头成为“自定义”。 这是一个问题,我不知道如何使自定义标头与 NestJS 和 CORS 一起使用,这就是为什么我在 NestJS 中尝试使用 CORS 选项进行很多操作,并在请求转到后端之前编写一些自定义标头,但没有成功,我收到相同的错误消息:

另一个 CORS 问题

我对这个问题有点困惑,CORS/CSRF 对于 spa 来说是一件大事,我的问题仍然相同,使用 CORS 和 SameSite cookie 属性,并且我的 api 位于我前端的子域中,是否真的有必要制作反 csrf 模式?

顺便说一句,我怎样才能使我的自定义标头正常工作以及为什么 CORS 对我说没有“Access-Control-Allow-Origin”标头,但有:

请求标头

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

零度° 2025-01-17 19:44:37

尝试生成 csrf 令牌并传递到每个请愿书

// 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()
})

的前面: https:// github.com/nestjs/nest/issues/6552#issuecomment-1175270849

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文