如何解决:“ForbiddenError: invalid csrf token”

时间:2021-01-24 18:36:21

标签: javascript node.js express csrf

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

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

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

  1. 我得到了一个 _csurf cookie(开发工具 > 应用程序 > cookie)
  2. 一个 csrf 令牌记录在我的控制台中(-> 查看从上下文中截取的最后一段代码)
  3. 当我发出 POST 请求时(-> 参见 routes/index.js f.ex.“/aignupQ”),我收到错误“无效的 csurf 令牌”;在请求标头中,我可以看到 _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,上面的代码片段正在运行。也许它可以帮助其他人..

2 个答案:

答案 0 :(得分:0)

编辑

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

原答案

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

解决方案 1

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

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

router.use(csrfProtection);

解决方案 2

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

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 :(得分:0)

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

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