How to solve: "ForbiddenError: invalid csrf token"
How to solve: "ForbiddenError: invalid csrf token"
我在设置 csrf 时遇到问题。我希望有人能指出我正确的方向。
我正在使用 next.js 和 express.js。
当我刷新页面时发生以下情况:
- 我得到一个 _csurf cookie(开发工具 > 应用程序 > cookie)
- 我的控制台中记录了一个 csrf 令牌(-> 请参阅从上下文中截取的最后代码)
- 当我发出 POST 请求时(-> 请参阅 routes/index.js f.ex. "/aignupQ"),我收到错误消息“无效的 csurf 令牌”;在请求中 header 我可以看到 _csrf cookie
- 当我刷新页面并再次发出 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}`);
});
});
routes/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
controllers/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,上面的代码片段可以正常工作。也许它可以帮助别人..
编辑
我还发现您的 api 路径可能有误。在你的 axios 中是 await axios.get('csrfToken')
。但是,我在您的路由器中看到 /api/csrfToken
。将其更改为 await axios.get('/api/csrfToken')
原答案
在 csurf 包中,当您多次在中间件中使用 csurf({cookie: true})
和 cookie 模式时,它会破坏 csrf 令牌作为响应 header 第一次 post。您可以在CSRF doesn't work on the first post attempt中查看更多详细信息,我在post中解释了原因。因此,您可以使用两种解决方案。
解决方案 1
根据评论,你在server.js
和router/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}
。当你使用session模式时,你可以在中间件中多次使用csruf({cookie: false})
。
我们需要在 header 中传递一个 cookie,如下所示:
headerMaps.put("cookie", "_csrf=value; connect.sid=value; csrfToken=value")
我在设置 csrf 时遇到问题。我希望有人能指出我正确的方向。
我正在使用 next.js 和 express.js。
当我刷新页面时发生以下情况:
- 我得到一个 _csurf cookie(开发工具 > 应用程序 > cookie)
- 我的控制台中记录了一个 csrf 令牌(-> 请参阅从上下文中截取的最后代码)
- 当我发出 POST 请求时(-> 请参阅 routes/index.js f.ex. "/aignupQ"),我收到错误消息“无效的 csurf 令牌”;在请求中 header 我可以看到 _csrf cookie
- 当我刷新页面并再次发出 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}`);
});
});
routes/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
controllers/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,上面的代码片段可以正常工作。也许它可以帮助别人..
编辑
我还发现您的 api 路径可能有误。在你的 axios 中是 await axios.get('csrfToken')
。但是,我在您的路由器中看到 /api/csrfToken
。将其更改为 await axios.get('/api/csrfToken')
原答案
在 csurf 包中,当您多次在中间件中使用 csurf({cookie: true})
和 cookie 模式时,它会破坏 csrf 令牌作为响应 header 第一次 post。您可以在CSRF doesn't work on the first post attempt中查看更多详细信息,我在post中解释了原因。因此,您可以使用两种解决方案。
解决方案 1
根据评论,你在server.js
和router/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}
。当你使用session模式时,你可以在中间件中多次使用csruf({cookie: false})
。
我们需要在 header 中传递一个 cookie,如下所示:
headerMaps.put("cookie", "_csrf=value; connect.sid=value; csrfToken=value")