【问题标题】:How to solve: "ForbiddenError: invalid csrf token"如何解决:“ForbiddenError: invalid csrf token”
【发布时间】:2021-04-28 15:37:51
【问题描述】:

我在设置 csrf 时遇到问题。我希望有人能指出我正确的方向。

我将 next.js 与 express.js 一起使用。

当我刷新页面时,会发生以下情况:

  1. 我得到一个 _csurf cookie(开发工具 > 应用程序 > cookie)
  2. 在我的控制台中记录了一个 csrf 令牌(-> 查看从上下文中截取的最后一个代码)
  3. 当我发出 POST 请求时(-> 参见 routes/index.js f.ex.“/aignupQ”),我收到错误“Invalid csurf token”;在请求标头中,我可以看到 _csrf cookie
  4. 当我刷新页面并再次发出 POST 请求时,一切正常。

我真的被这个错误弄糊涂了,真的不明白哪里出了问题。以下是一些相关代码:

server.js:

require("dotenv").config();
const express = require("express");
const next = require("next");
const bodyParser = require("body-parser");
const cors = require("cors");
const cookieParser = require("cookie-parser");
const routes = require('./routes');

const csrf = require("csurf");
const csrfProtection = csrf({
  cookie: true,
});


//next.js configuration
const dev = process.env.NODE_DEV !== "production";
const nextApp = next({ dev });
const port = 3000;
const handle = nextApp.getRequestHandler();

nextApp.prepare().then(() => {
  const app = express();

  app.use(bodyParser.json());
  app.use(bodyParser.urlencoded({ extended: false }));
  app.use(cors());
  app.use(cookieParser());
  app.use((err, req, res, next) => {
    res.status(500).send("Something went wrong!");
  });

  app.use(csrfProtection);
  app.use('/api', routes);

  app.get("*", (req, res) => {
    return handle(req, res);
  });

  //start server
  app.listen(port, (err) => {
    if (err) throw err;
    console.log(`listening on port ${port}`);
  });
});  


        

路由/index.js:

const express = require('express')
const router = express.Router()
const getCsrfToken = require('../controllers/csrf')
const postSignupQ = require("../controllers/postSignupQ");
const attachUser = require("../middleware/attachUser");

router.get("/csrfToken", getCsrfToken.getCsrfToken);
router.use(attachUser);
router.post("/signupQ", postSignupQ.postSignupQ);

module.exports = router

控制器/csrf.js

const getCsrfToken = (req, res) => {
  res.json({ csrfToken: req.csrfToken() }); 
};

module.exports = { getCsrfToken };

context - 这里是 console.log(csrf):

   useEffect(() => {
    const getCsrfToken = async() => {
      const { data } = await axios.get('api/csrfToken');
      console.log("csurf", data);
      axios.defaults.headers['X-CSRF-Token'] = data.csrfToken;
    };

    getCsrfToken();
  }, []); 
 

我不明白为什么我会收到错误消息,当我第一次发出 POST 请求以及刷新页面时一切正常。有什么问题,我该如何解决?

编辑
感谢 Jack Yu,上面的代码 sn-ps 正在运行。也许它可以帮助别人..

【问题讨论】:

  • 在您的server.js 中,您是否也使用了app.use(csrf({cookie: true}))
  • 是的。但我在 routes/index.js 中使用了它,而不是在 server.js 中。这是因为 routes/index.js 也是我调用令牌的地方。这会是个问题吗?感谢您查看我的代码!

标签: javascript node.js express csrf


【解决方案1】:

我们需要在 header 中传递一个 cookie,如下所示:

headerMaps.put("cookie", "_csrf=value; connect.sid=value; csrfToken=value")

【讨论】:

    【解决方案2】:

    编辑

    我还发现你的 api 路径可能是错误的。在你的 axios 中是await axios.get('csrfToken')。但是,我在您的路由器中看到了/api/csrfToken。改成await axios.get('/api/csrfToken')

    原答案

    在 csurf 包中,当您在中间件中多次使用带有 cookie 模式的 csurf({cookie: true}) 时,它会在 first time post 的响应标头中破坏 csrf 令牌。您可以在CSRF doesn't work on the first post attempt 中查看更多详细信息,我已经在那篇文章中解释了原因。因此,您可以使用两种解决方案。

    解决方案 1

    根据 cmets,您在 server.jsrouter/index.js 中使用 app.use(csruf({cookie: true}))。删除 router/index.js 中的以下行。当您在server.js 中设置csruf 时,您可以在controllers/csrf.js 中使用req.csrfToken() 而无需重新设置csruf。

    const csrf = require("csurf");
    const csrfProtection = csrf({
      cookie: true,
    });
    
    router.use(csrfProtection);
    

    解决方案 2

    您需要使用express-session 包。在csurf 之前添加以下代码。如果您的routes/index.js 中有.use(csrf({cookie: true})),请将其删除。

    const session = require('express-session')
    // mark1: change it to false
    const csrfProtection = csrf({
      cookie: false,
    });
    
    
    // blablabla ... other code
    
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({ extended: false }));
    app.use(cors());
    app.use(cookieParser());
    
    // mark2: put here
    app.use(session({
        name: "test",
        secret: "test",
        cookie: { maxAge: 3 * 60 * 60 * 1000 },
        resave: false,
        saveUninitialized: false
    }))
    // put here
    
    app.use((err, req, res, next) => {
        res.status(500).send("Something went wrong!");
    });
    
    app.use(csrfProtection);
    app.use('/api', routes);
    

    然后在所有 csurf 设置中将{cookie: true} 更改为{cookie: false}。使用会话模式时,可以在中间件中多次使用csruf({cookie: false})

    【讨论】:

    • 所以我正在尝试您的解决方案。解决方案 1:我从 routes/index.js 中删除了 router.use(csrfProtection)。所以我只在 server.js 中有它。但是,错误仍然存​​在-即使刷新页面也不起作用。解决方案 2:我收到错误“错误配置的 csrf”。有什么我可以尝试的想法吗?
    • 你能粘贴 server.js 吗?
    • 1.即使您重新启动服务器,第一次发布仍然会发生无效令牌? 2.我更新了我的答案,使用{cookie: false}时,你需要使用express-session包
    • 是的。我已经重新启动了服务器。即使使用 express-session 包,错误仍然存​​在。函数(端点和中间件)的顺序是否有可能是错误的?
    • @Ewax_Du 我用解决方案2更新了oder,有两个标记你需要修改试试看。
    猜你喜欢
    • 2017-05-21
    • 2023-03-22
    • 2016-05-05
    • 2016-09-16
    • 2021-03-26
    • 1970-01-01
    • 1970-01-01
    • 2020-03-25
    • 2013-04-11
    相关资源
    最近更新 更多