23 Nov 2017

Prevent CSRF attacks on login

Default blog image

The description on the OWASP site says: “Cross-Site Request Forgery (CSRF) is an attack that forces an end user to execute unwanted actions on a web application in which they're currently authenticated.”

This can happen when submitting data to the server, including logging in. That is why all services provide a state parameter on their authorize endpoint, e.g. see Box:
https://developer.box.com/v2.0/docs/oauth-20

This is Box’s description of what the state parameter is for: “An arbitrary string of your choosing that will be included in the response to your application. Anything that might be useful for your application can be included. Box roundtrips this information back to your application, and strongly recommends that you include an anti-forgery token, and confirm it in the response to prevent CSRF attacks to your users.”

Though the Forge Authentication API’s documentation does not list it, in case of our authorize endpoint as well you can add a state parameter that we will send back to you when calling your app’s Callback URL. That way you can make sure that the call came back as part of the current session.

You can choose a library that you think is best for generating a random value that you can pass in as the state value. Here is a server side code as an example:

// Return the forge authentication url
router.get('/user/authenticate', function (req, res) {
  // Generate a unique ID somehow and store it 
  // Here we're using the "cryptiles" npm package
  // but could be something else
  req.session.csrf = cryptiles.randomString(24);

  // Redirect the user to this page
  var url =
    "https://developer.api.autodesk.com" +
    '/authentication/v1/authorize?response_type=code' +
    '&client_id=' + config.credentials.client_id +
    '&redirect_uri=' + config.callbackURL +
    '&state=' + req.session.csrf + // adding the unique code
    '&scope=' + config.scopeInternal.join(" ");
  res.end(url);
});

// Wait for Autodesk callback (oAuth callback)
router.get('/api/forge/callback/oauth', function (req, res) {
  var csrf = req.query.state;

  // Checking if the unique code is still the same
  if (!csrf || csrf !== req.session.csrf) {
    res.status(401).end();
    return;
  }

  // etc.
});

Related Article