23

I use React Single Page Application as a client side or Create React App (CRA).

In my backend i use Node.js & Express.

to fetch data or store i need to call API from client to backend.

actually I've seen there are several middleware like: - Express CSURF

but to be honest I don't understand how to send a CSRF token to the client every request. I have tried several times, by inserting the CSRF into a cookie and then taking it on the client side. but when the first request or new cookie is stored, I get error Invalid CSRF Token.

and even though I did this:

app.use(session({
    genid: function (req) {
        return uuidv4() // use UUIDs for session IDs
    },
    name:keys.session.name,
    secret: keys.session.secret,
    resave: false,
    saveUninitialized: true,
    rolling:true,
    cookie: { 
        secure: false,
        httpOnly: true,
        maxAge:keys.session.maxAge, // satu hari,
        sameSite:true,
     }

}));
app.use(passport.session());
app.use(cookieParser());
app.use(csrf({ cookie: false }));


app.use((req,res,next)=>{
     res.cookie('CSRF_token', req.csrfToken(), { sameSite: true });
})

Which means the CSRF_token cookie will change each request. but I only set it once like this : axios.defaults.headers.common['csrf-token'] = csrf; and the results its still work, which should not working.

So do I need CSRF? or how to configure the correct one on react SPA.

Badis Merabet
  • 13,970
  • 9
  • 40
  • 55
Faris Dewantoro
  • 1,597
  • 4
  • 17
  • 31

1 Answers1

10

So do I need CSRF?

As stated here: Am I under risk of CSRF attacks in a POST form that doesn't require the user to be logged in? I think you should still set it.

As for why it is not working for you, I assume it is because of the header name. You may check what CSURF checks by default.

The default value is a function that reads the token from the following locations, in order:

  • req.body._csrf - typically generated by the body-parser module.
  • req.query._csrf - a built-in from Express.js to read from the URL query string.
  • req.headers['csrf-token'] - the CSRF-Token HTTP request header.
  • req.headers['xsrf-token'] - the XSRF-Token HTTP request header.
  • req.headers['x-csrf-token'] - the X-CSRF-Token HTTP request header.
  • req.headers['x-xsrf-token'] - the X-XSRF-Token HTTP request header.

Going by what CSURF checks, you have a variety of options to choose from, and looking at axios, it seems to have a couple options for setting xsrf cookie and header names.

...
// `xsrfCookieName` is the name of the cookie to use as a value for xsrf token
xsrfCookieName: 'XSRF-TOKEN', // default

// `xsrfHeaderName` is the name of the http header that carries the xsrf token value
xsrfHeaderName: 'X-XSRF-TOKEN', // default
...

For example, in order to use the X-XSRF-TOKEN header key axios comes with by default, I used the following method in my App.js file:

componentDidMount() {
    axios.get(`/api/csrf`) // Send get request to get CSRF token once site is visited.
      .then(res => {
        axios.defaults.headers.post['X-XSRF-TOKEN'] = res.data; // Set it in header for the rest of the axios requests.
      })
  }

You could, however, use a form hidden input or any other method you're comfortable with. You can read more about why it's common to put them in cookies here.

I'm not sure what you're using for your client, but if you're using Redux, you may look here for help. If it doesn't work, you may Google other methods.

yaserso
  • 2,638
  • 5
  • 41
  • 73
  • 9
    How safe is this? requesting the csrf token via an endpoint. Doesn't this defeat the purpose of a csrf token? – alex067 Mar 01 '20 at 01:40
  • @alex067 Ideally you would have a frontend server to inject the token in your frontend app. You're correct in the sense that this isn't the best solution, but I haven't been able to find a way to mitigate this without a frontend server. Otherwise you can sign the token in a jwt cookie to make sure it wasn't replaced. – yaserso Mar 01 '20 at 12:07
  • 4
    I've spent an entire day looking for a solution and I can't. No one appears to know how to do this properly. – Gary Nov 01 '21 at 05:16
  • > Make sure CSRF tokens can not be accessed with AJAX! Don't create a /csrf route just to grab a token, and especially don't support CORS on that route! This approach is not recommended according to this [guide](https://github.com/pillarjs/understanding-csrf). – Viktor Soroka May 18 '22 at 08:58