最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to solve: "ForbiddenError: invalid csrf token" - Stack Overflow

programmeradmin2浏览0评论

I have problems with setting up csrf. I hope that someone can point me in the right direction.

I'm using next.js with express.js.

When I refresh the page following happens:

  1. I get a _csurf cookie (dev tools > application > cookies)
  2. a csrf token is logged in my console (-> see last code snipped from context)
  3. when I make a POST request (-> see routes/index.js f.ex. "/aignupQ"), I get the error "Invalid csurf token"; in the request header I can see the _csrf cookie
  4. when I refresh the page and make the POST request again everything works.

I'm really confused by the error and really don't understand what is wrong. Here is some relevant code:

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 - here I 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();
  }, []); 
 

I don't understand why I get the error message, when I make a POST request for the first time and when I refresh the page everything works. What's the problem and how can I solve this?

EDIT
Thanks to Jack Yu the code snippets above are working. Maybe it can help someone else..

I have problems with setting up csrf. I hope that someone can point me in the right direction.

I'm using next.js with express.js.

When I refresh the page following happens:

  1. I get a _csurf cookie (dev tools > application > cookies)
  2. a csrf token is logged in my console (-> see last code snipped from context)
  3. when I make a POST request (-> see routes/index.js f.ex. "/aignupQ"), I get the error "Invalid csurf token"; in the request header I can see the _csrf cookie
  4. when I refresh the page and make the POST request again everything works.

I'm really confused by the error and really don't understand what is wrong. Here is some relevant code:

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 - here I 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();
  }, []); 
 

I don't understand why I get the error message, when I make a POST request for the first time and when I refresh the page everything works. What's the problem and how can I solve this?

EDIT
Thanks to Jack Yu the code snippets above are working. Maybe it can help someone else..

Share Improve this question edited Jan 26, 2021 at 16:42 Ewax_Du asked Jan 24, 2021 at 18:36 Ewax_DuEwax_Du 4652 gold badges14 silver badges26 bronze badges 2
  • In your server.js, did you also use app.use(csrf({cookie: true})) ? – Jack Yu Commented Jan 25, 2021 at 6:53
  • 1 Yes. But I used it in routes/index.js, not in server.js. This is because routes/index.js is also the place where I make a call for the token. Could this be a problem? Thanks for taking look at my code! – Ewax_Du Commented Jan 25, 2021 at 8:39
Add a ment  | 

2 Answers 2

Reset to default 2

EDIT

I also found your api path might be wrong. In your axios is await axios.get('csrfToken'). But, I saw /api/csrfToken in your router. Change it to await axios.get('/api/csrfToken')

Original Answer

In csurf package, when you use csurf({cookie: true}) with cookie mode in middleware at multiple times, it'll break the csrf token in response header with first time post. You could take a look for more detail in CSRF doesn't work on the first post attempt, I've explain the reason in that post. So, there are two solutions you could use.

Solution 1

According to the ments, you use app.use(csruf({cookie: true})) in server.js and router/index.js. Remove the following line in your router/index.js. When you setup csruf in server.js, you could use req.csrfToken() in controllers/csrf.js without setting up csruf again.

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

router.use(csrfProtection);

Solution 2

You'll need to use express-session package. Add following code before the csurf. If you have .use(csrf({cookie: true})) in your routes/index.js, remove it.

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

Then change {cookie: true} to {cookie: false} in all csurf setting. When you use session mode, you could use csruf({cookie: false}) many times in middleware.

We need to pass a cookie in header like below:

headerMaps.put("cookie", "_csrf=value; connect.sid=value; csrfToken=value")
发布评论

评论列表(0)

  1. 暂无评论