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?
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:
A possible solution:
forceNew=false
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.
Copy link to clipboard
Copied
Still happening @BKBK. Short of creating my own generation and validation, even tried removing custom tokenKey argument).
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.
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.
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;
}
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.
Copy link to clipboard
Copied
That's fine.
I am curious to know the result. 🙂