RequestVerificationToken not always rendered for custom forms when caching is enabled

Topics: Troubleshooting
Jan 15, 2013 at 1:00 AM

Just putting this out there to see if anyone else is have similar issues. 

I'm occasionally seeing missing requestverificationtoken errors when submitting forms on some of my sites, and upon further inspection it appears on occasion the requestverificationtoken hidden field is not being output at all.

I'm using caching and I suspect this may be playing a part. Has anyone experienced similar issues?

Coordinator
Jan 15, 2013 at 1:17 AM

Does it come back if you do a CTRL+F5?

Jan 15, 2013 at 1:19 AM

Yes generally it does. Is there some kind of local browser caching going on?

Why would it not be rendered at all? I'd expect maybe an older (cached) token to be rendered, but not a completely missing token?

Coordinator
Jan 15, 2013 at 1:22 AM

There is some specific code in the caching module around the anti-forgery token, because it needs to be generated anew even if the rest of the page is cached. It looks like there is a flaw in that code. Please file a bug, with as many details as you can about how to reproduce it.

CTRL+F5 not only bypasses the client cache, it also sends a no-cache header that instructs the server not to use any cached version it has.

Jan 15, 2013 at 2:18 AM
Edited Jan 15, 2013 at 2:19 AM

I think this is something I've introduced with a change to improve the AntiForgeryAuthorizationFilter (Refer to point 2 in the discussion at http://orchard.codeplex.com/discussions/406844).

Part of the change I made was to ensure anti forgery token is always validated even for non-authenticated users.

Here's the pull request I made (I have the changes locally on my own deployment)...

http://orchard.codeplex.com/SourceControl/network/forks/mjy78/Issue19384/contribution/3876

The problem is that the code in the cache module in Contrib.Cache.Filters.OutputCacheFilter.cs has a section commented out with a note that there is no need to replace the antiforgerytoken as it is not used with unauthenticated requests at this point. However the change above that I applied means that anti forgery token is enforced with unauthenticated requests.

Uncommenting this section (approx line 216) ensures that the antiforgerybeacon is correctly replaced with a fresh anti forgery token.

The thing I still don't understand is what the reason is behind not enforcing anti-forgery tokens for unauthenticated form requests? Is it purely historical because initially forms were only submitted by authenticated users? Now with custom forms it's quite common for unauthenticated users to be submitting forms.

I think the fix for this problem is to change the code in OutputCacheFilter.cs from this...

 

                /* 
                 * 
                 * There is no need to replace the AntiForgeryToken as it is not used for unauthenticated requests
                 * and at this point, the request can't be authenticated
                 *
                 * 

                // replace any anti forgery token with a fresh value
                if (output.Contains(AntiforgeryBeacon))
                {
                    var viewContext = new ViewContext
                    {
                        HttpContext = filterContext.HttpContext,
                        Controller = filterContext.Controller
                    };

                    var htmlHelper = new HtmlHelper(viewContext, new ViewDataContainer());
                    var siteSalt = _workContext.CurrentSite.SiteSalt;
                    var token = htmlHelper.AntiForgeryToken(siteSalt);
                    output = output.Replace(AntiforgeryBeacon, token.ToString());
                }

                 */

 

to this...

 

                // replace any anti forgery token with a fresh value
                if (output.Contains(AntiforgeryBeacon))
                {
                    var viewContext = new ViewContext
                    {
                        HttpContext = filterContext.HttpContext,
                        Controller = filterContext.Controller
                    };

                    var htmlHelper = new HtmlHelper(viewContext, new ViewDataContainer());
                    var token = htmlHelper.AntiForgeryToken();
                    output = output.Replace(AntiforgeryBeacon, token.ToString());
                }    

 

Note. The call the htmlHelper.AntiForgeryToken(withSalt) is deprecated in MVC 4 so I've changed it to not include the site salt.

I've tested this, and the problem is resolved with the above change (and allows me to keep the initial change I made to enforce anti-forgery on unauthenticated requests)

Coordinator
Jan 15, 2013 at 6:58 PM

Thanks.

Jan 15, 2013 at 10:38 PM

I've raised an issue and generated a pull request for the Orchard Cache module for the above change.

http://orchardcache.codeplex.com/workitem/23

http://orchardcache.codeplex.com/SourceControl/network/forks/mjy78/Issue23/contribution/3926