Blog

Understanding CSRF Attacks

While OAuth offers a powerful framework for secure authorization across various services, it is not infallible from vulnerabilities. Login CSRF attacks are one instance of vulnerabilities that may arise when using OAuth, and the nonce technique functions as an effective tool to defend against potential breaches.


Introduction 

For application developers, having a comprehensive understanding of security vulnerabilities and proactive measures used to protect their systems is essential. This post explores the specifics of login CSRF attacks, providing insight into their mechanics as well as demonstrating how nonce, an acronym for "number used once" to describe a unique random string, can be used to safeguard your application’s security. 

What Is a CSRF Attack?

A Cross-Site Request Forgery (CSRF) attack is a type of security vulnerability that tricks a user into unwittingly performing actions on a web application to which they are authenticated. Unlike other types of attacks, CSRF exploits the trust that a website has in the user’s browser, circumventing the need to target the user.

Practical Example: Login CSRF

During a login CSRF attack, an attacker deceives a user into unknowingly logging into the attacker’s account, often with the intent to collect private information from users who mistakenly believe they’re accessing their own account.

An attack scenario might look like this: 

  1. The Attacker sends a crafted link or embeds a malicious request, aiming to trick the User into clicking or loading it.
  2. The User clicks on the link or loads the malicious content, often unknowingly initiating a login request to the application, but with credentials or context for the Attacker's account.
  3. The application processes the request, thinking the User wants to login to the Attacker's account.
  4. The User is logged into the Attacker's account on the application.

Login CSRF attacks are generally considered less severe than other types of attacks, because many users will recognize that they’re logged into the wrong account. However, the consequences of a successful attack can still be significant, since users may not realize the mistake until it’s too late.

Preventing Login CSRF Attacks: the Nonce Technique

Enter the 'nonce' technique—a method that has gained traction for its efficacy in mitigating the risk of CSRF attacks during OAuth flows. A nonce is a unique, random string that is generated for each OAuth process. It functions as a CSRF token, securely validating that the client application making the request is the one to which the server responds. This process involves generating a nonce, hashing it for secure storage, and validating it server-side to ensure the integrity of the OAuth flow.

Here's how you might use this technique:

  1. Generate a random value (nonce): Use your server-side language of choice to generate a secure random value, also known as a nonce. This value will be used later to prevent CSRF attacks. In this example we’ll use Node’s built-in crypto library:


import crypto from 'crypto';

const generateNonce = (): string => {
return crypto.randomBytes(16).toString('base64');
};


  1. Store the hashed value: After generating the nonce, hash it and store it in a short-lived session cookie within the user's browser.



const hashNonce = (nonce: string): string => {
return crypto.createHash('sha256').update(nonce).digest('hex');
};

res.cookie('hashed_nonce', hashNonce(generateNonce()), {maxAge: 5 * 60 * 1000);


	
 
  1. Pass the nonce in the state parameter: During the OAuth authorization process, send the nonce as a state parameter to the OAuth authorization server. Here, the original nonce (not the hashed one) should be included in the state parameter when redirecting the user.



const generateOAuthURL = (nonce: string): string => {
  const url = new URL('https://auth-server.com/oauth/authorize');

  url.searchParams.set('clientID', 'your_client_id_here');
  url.searchParams.set('redirectURI', 'your_callback_url_here');
  url.searchParams.set('state', nonce);

  return url.toString();
};


	
 

With WorkOS:





const WorkOS = require('@workos-inc/node').default;
const workos = new WorkOS('your_workos_api_key');

app.get('/auth', (_req, res) => {
 const nonce = generateNonce();

 // The user's organization ID
 const organization = 'org_123';

 // The callback URI WorkOS should redirect to after the authentication
 const redirectURI = 'https://dashboard.my-app.com';

 const authorizationUrl = workos.sso.getAuthorizationURL({
   organization,
   redirectURI,
   clientID,
   state: nonce,
 });

 res.redirect(authorizationUrl);
});


 
 	
  1. Server-side validation: Upon the user's return to your application, validate the nonce to ensure it is legitimate and thus prevent CSRF attacks. The nonce received in the state parameter is hashed and compared to the hashed nonce stored in the session cookie.


  
const crypto = require('crypto');

const callbackHandler: express.RequestHandler = (req, res) => {
 const returnedState = req.query.state as string;
 const hashedReturnedState = hashNonce(returnedState);

 // Get your cookie value
 const storedHashedNonce = req.cookies['hashed_nonce'];

 if (crypto.timingSafeEqual(hashedReturnedState, storedHashedNonce)) {
   // Nonce is valid, proceed with the OAuth flow
 } else {
   // Possible CSRF attack, halt the process
   res.status(403).send('Authentication failed');
 }
};


  
  
 
 

With WorkOS:



  
app.get('/auth/callback', async (req, res) => {
 const returnedState = req.query.state as string;
 const hashedReturnedState = hashNonce(returnedState);

 // Get your cookie value
 const storedHashedNonce = req.cookies['hashed_nonce'];

  // Define your client ID
 const clientID = 'your_client_id_here';

 if (hashedReturnedState === storedHashedNonce) {
   const code = req.query.code as string;

   try {
     const profileAndToken = await workos.sso.getProfileAndToken({ code, clientID });
     // Use the information in `profile` for further business logic.
     res.redirect('/');
   } catch (err) {
     res.status(500).send('WorkOS profile and token retrieval failed');
   }
  
 } else {
   // Potential CSRF attack detected
   res.status(403).send('Authentication failed');
 }
});




 
 	

Conclusion

OAuth offers a powerful framework for secure and smooth authorization across various services, but it is not infallible from vulnerabilities. It is crucial for developers to fully grasp these potential risks to effectively prevent unauthorized access and other malicious activities.

The nonce is an effective tool for mitigating vulnerabilities that may arise from OAuth flows. In addition to defensive mechanisms like the nonce, continuous updates, user education, and persistent monitoring can strengthen your application’s security from potential breaches.

In this article

This site uses cookies to improve your experience. Please accept the use of cookies on this site. You can review our cookie policy here and our privacy policy here. If you choose to refuse, functionality of this site will be limited.