• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
0

verifyCSRFToken fails randomly

Explorer ,
Apr 11, 2022 Apr 11, 2022

Copy link to clipboard

Copied

Environment: CF2016 + patch17

OS: Windows

Session Enabled and hosted via Elasticache (Redis)

Multi-instance environment

Load: A LOT

 

 

Given the two functions:

/**
 * Method used to create a CSRF token, mainly used for login forms - Returns string
 *
 * tokenKey - optional, string. Unique key. Must also be used for decoding the token
 * forceNew - optional, boolean. Defualt to true. Will create new token each time the method is called
 **/
public string function generateCSRFToken( string tokenKey='some-special-key', boolean forceNew=true ){
	return CSRFGenerateToken(  arguments.tokenKey, arguments.forceNew );
}

/**
 * Method used to verify a CSRF token, mainly used for login forms - Returns string
 *
 * token - required, string. The token to be verified
 * tokenkey - optional, string. Defualt to true. Will create new token each time the method is called
 **/
public boolean function verifyCSRFToken( required string token, string tokenKey='some-special-key' ){
	return CSRFVerifyToken( arguments.token, arguments.tokenKey ) ? true : false;
}

 

The way I understood the CSRFGenerateToken() function, it creates a token and sticks it into the session. Given that our multi-instance environment is using the same Elasticache service (to prevent session duplication) and so the session is shared on all instances, I would assume that the CSRFVerifyToken() would not have an issue verifying the token (presummably that is in the session). For some very ODD reason, it fails for random users - I don't have any debug information other than when the CSRFVerifyToken() function is called, it returns false. 

 

Thoughts?

Views

422

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Apr 12, 2022 Apr 12, 2022

Copy link to clipboard

Copied

@--jojo-- , I think the way you understand CSRFGenerateToken() is correct. But I also think the design with the two custom functions isn't optimal.

 

Under the covers, ColdFusion may non-transparently change sessions. Which shouldn't be a bother at all. Requests will then be handled seamlessly by the next session.

 

However, with your design, a situation could arise where the session change occurs between the call generateCSRFToken() and the call verifyCSRFToken(). By virtue of

 

forceNew=true

 

the following sequence of events might happen:

  1. Session S: the caller of verifyCSRFToken() obtains a token Ts with which to call the function;
  2. Session changes from S to S+1;
  3. The caller of generateCSRFToken() calls the function, generating a new token Ts+1;
  4. The caller of verifyCSRFToken() calls the function, passing it the token Ts.

 

A possible solution:

 

forceNew=false

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Apr 12, 2022 Apr 12, 2022

Copy link to clipboard

Copied

Thanks @BKBK What you suggested makes sense - Will definitely try what you've suggested and hopefully will solve our issue.  Will report back.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Apr 13, 2022 Apr 13, 2022

Copy link to clipboard

Copied

Still happening @BKBK. Short of creating my own generation and validation, even tried removing custom tokenKey argument). 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Apr 13, 2022 Apr 13, 2022

Copy link to clipboard

Copied

Also, I was curious to see what exactly is being injected into the session so I dumped out the contents of the session - nothing that I saw pertained to CSRF. 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Apr 14, 2022 Apr 14, 2022

Copy link to clipboard

Copied

 

Also, I was curious to see what exactly is being injected into the session so I dumped out the contents of the session - nothing that I saw pertained to CSRF. 


By @--jojo--

 

As I understand it, that is the way it's supposed to be. You don't see it when you dump the session because it is not a session variable. However, the token is attached to the session. This is actually the basis of my next suggestion, which follows.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Apr 14, 2022 Apr 14, 2022

Copy link to clipboard

Copied

Still happening @BKBK. Short of creating my own generation and validation, even tried removing custom tokenKey argument). 

By @--jojo--

No need for all that. The cause is obvious: the 2 custom functions. A myriad of things can happen between the two calls.

 

Call 1: you order a Pepperoni, and look forward to its delivery to your home in a quarter of an hour.

Call 2: the pizza service hires a delivery service to do deliveries.

 

Fast forward.

Your pizza arrives an hour late, and is barely lukewarm. Besides, it's a Chicago pizza instead of a Pepperoni!

 

So what happened? Traffic delay? Client-pizza mix-up by the delivery servce? Who knows?

 

All we know is that that can happen because the two services are separate. Separation of concerns is a handy design pattern, but not for this use-case.

 

A possible solution is tracking. The pizza delivery would then be equipped with a tracker, which would be aware of the travel times and location details of the customer.

 

To borrow from that idea, we could use the session as a tracker. The following code has the functionality of your original code. But, in addition, it keeps track of discrepancies.

 

public string function generateCSRFToken( string tokenKey='some-special-key', boolean forceNew=false ){
	session.tokenKey=arguments.tokenKey;
	session.CSRFToken=CSRFGenerateToken( arguments.tokenKey, arguments.forceNew );

	return session.CSRFToken;
}

public boolean function verifyCSRFToken( required string token, string tokenKey='some-special-key' ){

	var isTokenVerified=CSRFVerifyToken( arguments.token, arguments.tokenKey ) ;
	var CSRFTracker=structnew();

	// Track any differencies and log them for later review
	if ( compare(session.CSRFToken, arguments.token) != 0 || compare(session.tokenKey, arguments.tokenKey)  != 0 )  {
		CSRFTracker.session.CSRFToken=session.CSRFToken;
		CSRFTracker.arguments.token=arguments.token;
		CSRFTracker.session.tokenKey=session.tokenKey;
		CSRFTracker.arguments.tokenKey=arguments.tokenKey;

		writedump(var=CSRFTracker, format="html", output="#server.coldfusion.rootDir#\logs\CSRFTracker.html");
	}

	return isTokenVerified;

}

 

 

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Apr 14, 2022 Apr 14, 2022

Copy link to clipboard

Copied

 @BKBK - I honestly had (somewhat) the same idea this morning - injecting the generated csrf token into the session, then on verify, if the standard verification fails, compare the arguments token to the session token (if it exists). Great idea - we currently have a logger that I can throw the log to rather than creating a file. 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Apr 15, 2022 Apr 15, 2022

Copy link to clipboard

Copied

LATEST

That's fine.

I am curious to know the result. 🙂

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Resources
Documentation