cbCsrf module - token reset

I’m struggling with getting the cbCsrf module to work properly. The issue is that the tokens seem to be resetting every few minutes in the user session. I set up some logging/debugging and it appears the issue is the CacheStorage . After a few minutes, when the generate() function is eventually run from the csrf() helper function, the csrf data struct, which is pulling the token out of the session, is empty and so a new token gets generated. This happens with the default settings and when I bump the cbcsrf and/or cache timeouts up. Am I just using this module incorrectly?

Can you post your config settings for it?
Engine
ColdBox Version
Code using it

Sure. Coldbox.cfc config is:

cbcsrf : {
	// By default we load up an interceptor that verifies all non-GET incoming requests against the token validations
	enableAutoVerifier : true,
	// A list of events to exclude from csrf verification, regex allowed: e.g. stripe\..*
	verifyExcludes : [],
	// By default, all csrf tokens have a life-span of 30 minutes. After 30 minutes, they expire and we aut-generate new ones.
	// If you do not want expiring tokens, then set this value to 0
	rotationTimeout : 480,
	// Enable the /cbcsrf/generate endpoint to generate cbcsrf tokens for secured users.
	enableEndpoint : false,
	// The WireBox mapping to use for the CacheStorage
	cacheStorage = "CacheStorage@cbstorages",
	// Enable/Disable the cbAuth login/logout listener in order to rotate keys
	enableAuthTokenRotator : false
}

I’ve also tried 0 for the timeout with same problem… then realized it was an issue with the cacheStorage. The cacheStorage is using the default settings, though I played with upping the timeout there, but it doesn’t affect anything.

I’m adding it in various views, below the form element, using the helper function #csrf()# to generate the hidden input field.

FWIW, if I dump the data, I can see that the “expires” value is accurate (480 minutes in this case).

Lucee@5.3.8+206
coldbox-6.5.2+37-202107141455
cbcsrf-2.3.0+6
cbstorages-2.7.0+9

Hmm, ok, do me a favor. Paste your config/cachebox.cfc config please

I do not have a Cachebox.cfc in config folder.

Ok, that’s the issue then. The default ocnfiguration for the template cache for ColdBox uses ConcurrentSoftReferenecs which never guarantee timeouts. The JVM can blatenly remove stuff as it needs.

The default config is this:

/**
 * Copyright Since 2005 ColdBox Framework by Luis Majano and Ortus Solutions, Corp
 * www.ortussolutions.com
 * ---
 * The default ColdBox CacheBox configuration object for ColdBox Applications
 */
component {

	/**
	 * Configure CacheBox for ColdBox Application Operation
	 */
	function configure(){
		// The CacheBox configuration structure DSL
		cacheBox = {
			// LogBox config already in coldbox app, not needed
			// logBoxConfig = "coldbox.system.web.config.LogBox",

			// The defaultCache has an implicit name "default" which is a reserved cache name
			// It also has a default provider of cachebox which cannot be changed.
			// All timeouts are in minutes
			defaultCache : {
				objectDefaultTimeout           : 120, // two hours default
				objectDefaultLastAccessTimeout : 30, // 30 minutes idle time
				useLastAccessTimeouts          : true,
				reapFrequency                  : 5,
				freeMemoryPercentageThreshold  : 0,
				evictionPolicy                 : "LRU",
				evictCount                     : 1,
				maxObjects                     : 300,
				objectStore                    : "ConcurrentStore", // guaranteed objects
				coldboxEnabled                 : true
			},
			// Register all the custom named caches you like here
			caches : {
				// Named cache for all coldbox event and view template caching
				template : {
					provider   : "coldbox.system.cache.providers.CacheBoxColdBoxProvider",
					properties : {
						objectDefaultTimeout           : 120,
						objectDefaultLastAccessTimeout : 30,
						useLastAccessTimeouts          : true,
						reapFrequency                  : 5,
						freeMemoryPercentageThreshold  : 0,
						evictionPolicy                 : "LRU",
						evictCount                     : 2,
						maxObjects                     : 300,
						objectStore                    : "ConcurrentSoftReferenceStore" // memory sensitive
					}
				}
			}
		};
	}

}

So what I suggest, is add a confgi/Cachebox.cfc and change the template cache to a more durable storage like : ConcurrentStore

I’ve always understood that file to be optional and never before had a need to modify its settings.

I will give this a try, thanks!

It is completely optional.

The default is to protect the jvm from ever growing caches. The drawback is that control is surrendered. @bdw429s thoughts?

Added the Cachebox.cfc and changed the ‘template’ cache from ConcurrentSoftReferenceStore to ConcurrentStore. Restarted the server in Commandbox. Same issue.

I am, admittedly, ignorant about cb caching. Should I be creating a separate named cache in cachebox.cfc just for this module’s functionality, rather than changing the ‘template’ cache from ConcurrentSoftReferenceStore to ConcurrentStore?

I was hoping to manage token rotation from the rotationTimeout setting in the config - does this mean that I need to be aware of 2 competing timeouts for these tokens? Is there a reason we’re unable to use the sessionStorage cbStorage module which would seem to more align with a session-based token (I’m only using a single server instance)?

So I just created a new app via commandbox using the AdvancedScript. I then installed the cbCsrf module. Then changed the ‘template’ cache from ConcurrentSoftReferenceStore to ConcurrentStore in Cachebox.cfc.

I add this to the main layout right above the closing body tag to easily test/view:

<pre>
	#session.sessionid#<br>
	#encodeForHTML( csrf() )#
</pre>
<cfdump var="#getInstance( 'cacheStorage@cbcsrf' ).get( '$CSRFTokens' )#">

Enable JEE session in server admin and start the server. After a few minutes I click on the home button and notice that the token has rotated.

I am perplexed of why your tokens would rotate like that. You will have to do some more debugging.

The explicit rotate() method is ONLy automatically used by the cbauth module, because cbcsrf listens to the postAuthentication() and postLogout interceptors and rotates automatically upon login and logout of an app. Plus you would need the enableAuthTokenRotator to be true in order for rotation to be automatic.

So, if that’s not the case. Then the only reason they would rotate is that the session identifier changed. If you use the default of CacheStorage then it will use the template cache for storage. If you moved to Concurrent Store then they will be respected and timeouts honored.

So we are down to two possible scenarios:

  • A fwreinit ocurred, which completely wipes everything in memory
  • The session identifiers changed by CFML, unusual but it can happen

The CacheStorage leverages the getSessionKey() method to get the current identifier for a user:

/**
	* Builds the unique Session Key of a user request and returns it to you.
	*/
	string function getSessionKey(){
		var prefix = "cbstorage:#variables.appName#:";

		// Check jsession id First
		if( isDefined( "session" ) and structKeyExists( session, "sessionid" ) ){
			return "cbstorage:" & session.sessionid;
		}
		// Check normal cfid and cftoken in cookie
		else if( structKeyExists( cookie, "CFID" ) AND structKeyExists( cookie,"CFTOKEN" ) ){
			return prefix & hash( cookie.cfid & cookie.cftoken );
		}
		// Check normal cfid and cftoken in URL
		else if( structKeyExists( URL, "CFID" ) AND structKeyExists( URL,"CFTOKEN" ) ){
			return prefix & hash( URL.cfid & URL.cftoken );
		}
		// check session URL Token
		else if( isDefined( "session" ) and structKeyExists( session, "URLToken" ) ){
			return prefix & session.URLToken;
		} else {
			throw(
				message = "Cannot find a jsessionid, URLToken or cfid/cftoken in any scope. Please verify",
				type 	= "CacheStorage.UniqueKeyException"
			);
		}
	}

So if that changed, the csrf tokens change. I would probably say you need to add some debugging dumps or logs, surrouding the retrieval of the keys and if something is calling your rotation methods.

Thanks Luis.

The session ID does NOT change, if you look at the code I pasted above, I dump both the session id and the token. The session id stays the same throughout, but the token changes after a few minutes of inactivity.

Also, it seems clear from the logging I’ve already done that the issue is occurring because the cacheStorage is “losing” the stored token first, which then causes the module to generate a new one and not the other way around. (i.e. this seems more of a cacheStorage issue than a cbCsrf module issue.)

As noted in my previous post, this is easily duplicated on a new site created with AdvancedScript template and adding the cbCsrf module, so I don’t see how it can be something different on my site, unless I am completely using it wrong out of the box? (no pun intended)

Re: the getSessionKey() method - I noticed that and it seems to be the reason why I cannot change from the cacheStorage module to the sessionStorage module (which I assume would fix MY issue, though probably not provide the flexibility that the module wants to provide.) That is, I tried to set the cacheStorage for this module to sessionStorage@cbstorages but it throws an error because the getSessionKey() method is expected.

From what I can gather, this is what is happening:

  1. call csrf() helper function, which calls csrfToken() method;
  2. csrfToken() returns the cbcsrf object and generates a token using the generate() method;
  3. the generate() method grabs the session data if it exists and returns it and if it does not exist, it creates a new token and returns it.

I sprinkled several logging statements into this generate() method and can see that after a few minutes of inactivity, the data coming from this line is empty:

49: var csrfData = cacheStorage.get( getTokenStorageKey(), {} );

it has the correct data at first, but after few minutes, it “loses” it. That’s why i thought your original answer about using a different cache store might work (even though I know really nothing about how that’s working!)

So not really sure where to go from here?

This may be a silly question, but are you reinitting the framework? That will cause the caches to be re-created from scratch.

Thanks Brad. I am not reinitting the framework.

I would assume then that it’s disappearing from the cache when the object timeout is reached. The cbstorage’s cachestorage will default the default timeout to 60 minutes

but only if you don’t have a setting like the CacheBox.cfc example above:

objectDefaultTimeout           : 120,
objectDefaultLastAccessTimeout : 30,

Also, have you been using the cache panel in the cbdebugger which will show you all the items in the cache as well as their timeout. If you are wanting these to live forever, I’d declare another named cache with a default timeout of 0.

Well, I’m not sure what to say… I just again did the following (and on a different PC):

  1. use commandbox to create a brand new app using AdvancedScript;
  2. use commandbox to install the cbCsrf module;
  3. add the same cbcsrf module settings to config\Coldbox.cfc (changing rotationTimeout to 480
  4. change the template objectStore from ConcurrentSoftReferenceStore to ConcurrentStore as recommended above by Luis - everything else left at their default settings;
  5. add above mentioned code to the layout to dump the session id and the csrf data;
  6. start the server and set session to JEE then restart the server (screenpic 1);
  7. wait ~5 minutes, then click home button again and the token is different (screenpic2)

You can see the times in these 2 screenshots:

first_boot

after 5 minute wait:

click_home_5min

You can see that the session id remains the same, but the token changes. The created dates show the difference in time passed.

While your comment to create a new cache with timeout of 0 could perhaps be an option, it seems that the default timeout is 30 minutes and things are changing after only 5 minutes? Also, it seems to me that the token rotation interval ought to be controlled by the rotationTimeout setting the module provides… having to worry about multiple, competing timeouts seems wrong (of course, if that’s what needs be done, then I’ll do it… but right now I cannot even get the default to work without losing the token in 5 minutes…)

I installed cbdebugger with the cachebox panel enabled and restarted server and ran test again. Here are the screen shots of the results:

first_boot_csrf

No activity at all and then click home button after ~5 minutes:
after_5min_csrf

in the second screenshot of the cachebox debugger I don’t see any cbstorage items at all. Also, did you have the template cache selected when you were looking or were you looking at the default cache?

The timeout in the first screenshot does seem to be 60 minutes. Makes me wonder if Cachebox is reaping the cache and removing the items too soon.

Apologies Brad - I had template cache selected in first one, here is the template for the send one:

The cbstorage is completely gone and so there’s the issue I guess, but not sure why?

I would have expected there would be a new cbstorage key if the token was re-created and stored in the cache again :thinking: