<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Diego Marcet &#187; MVC</title>
	<atom:link href="http://blogs.southworks.net/dmarcet/category/mvc/feed/" rel="self" type="application/rss+xml" />
	<link>http://blogs.southworks.net/dmarcet</link>
	<description></description>
	<lastBuildDate>Mon, 26 Apr 2010 16:17:47 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Working together with AntiForgeryToken and OutputCache on ASP.NET MVC</title>
		<link>http://blogs.southworks.net/dmarcet/2010/04/26/working-together-with-antiforgerytoken-and-outputcache-on-aspnet-mvc/</link>
		<comments>http://blogs.southworks.net/dmarcet/2010/04/26/working-together-with-antiforgerytoken-and-outputcache-on-aspnet-mvc/#comments</comments>
		<pubDate>Mon, 26 Apr 2010 14:57:43 +0000</pubDate>
		<dc:creator>dmarcet</dc:creator>
				<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[MVC]]></category>

		<guid isPermaLink="false">http://blogs.southworks.net/dmarcet/2010/04/26/working-together-with-antiforgerytoken-and-outputcache-on-aspnet-mvc/</guid>
		<description><![CDATA[... <a href="http://blogs.southworks.net/dmarcet/2010/04/26/working-together-with-antiforgerytoken-and-outputcache-on-aspnet-mvc/" class="more-link">read more<img src="http://blogs.southworks.net/dmarcet/wp-content/themes/southworks/assets/img/arrow-blue.png" width="12" height="12" alt="" /></a>]]></description>
			<content:encoded><![CDATA[<p>If you develop using ASP.NET MVC you&#8217;ll probably know the <a href="http://msdn.microsoft.com/en-us/library/dd492767.aspx" target="_blank">AntiForgeryToken</a> helper method, used as a protection mechanism for <a href="http://en.wikipedia.org/wiki/Cross-site_request_forgery" target="_blank">cross-site request forgery</a>.</p>
<p>This mechanism consists of two pieces: a cookie<strong> (1)</strong> and a hidden field included on the form to be submitted<strong> (2)</strong>.</p>
<p><a href="http://blogs.southworks.net/dmarcet/files/2010/04/image5.png"><img src="http://blogs.southworks.net/dmarcet/files/2010/04/image5-thumb.png" border="0" alt="image" width="620" height="347" /></a></p>
<p>When submitting to a Controller&#8217;s Action annotated with the [<a href="http://msdn.microsoft.com/en-us/library/system.web.mvc.validateantiforgerytokenattribute.aspx" target="_blank">ValidateAntiForgeryToken</a>] attribute, the MVC framework will compare the values from the Cookie and the Hidden field (which could match partially only) and decide whether the request is valid or not.</p>
<p>The problem with this two-steps mechanism is that it doesn&#8217;t get along very well with cache (at least not with caching&#8217;s default behavior). Imagine that the action that renders the page with the <em>AntiForgeryToken</em> helper method invocation is marked with the [<a href="http://msdn.microsoft.com/en-us/library/system.web.mvc.outputcacheattribute.aspx" target="_blank">OutputCache</a>] attribute, this means that unless you take into account cookie generation, you may be returning a cached version of the page (with the hidden field value set) even though the cookie has not been sent to the requesting user.</p>
<p>In order to avoid this issue you can use the <a href="http://msdn.microsoft.com/en-us/library/system.web.mvc.outputcacheattribute.varybycustom.aspx" target="_blank">VaryByCustom</a> property on the OutputCache attribute:</p>
<div id="28e5c67c-e95c-460d-bb0a-5738e1df5749" class="wlWriterEditableSmartContent" style="padding-bottom: 0px;margin: 0px;padding-left: 0px;padding-right: 0px;float: none;padding-top: 0px">
<pre><span style="color: #000000">[OutputCache(
  Location </span><span style="color: #000000">=</span><span style="color: #000000"> OutputCacheLocation.ServerAndClient,
  Duration </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #800080">600</span><span style="color: #000000">,
  VaryByParam </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #800000">"</span><span style="color: #800000">none</span><span style="color: #800000">"</span><span style="color: #000000">,
  VaryByCustom </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #800000">"</span><span style="color: #800000">RequestVerificationTokenCookie</span><span style="color: #800000">"</span><span style="color: #000000">)]
</span><span style="color: #0000FF">public</span><span style="color: #000000"> ActionResult Index()
{
  </span><span style="color: #0000FF">return</span><span style="color: #000000"> </span><span style="color: #0000FF">new</span><span style="color: #000000"> View();
}</span></pre>
<p><!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --></p>
</div>
<p>And then program the rule on the <em>global.asax</em>&#8216;s <a href="http://msdn.microsoft.com/en-us/library/system.web.httpapplication.getvarybycustomstring.aspx" target="_blank">GetVaryByCustomString</a> method:</p>
<div id="defd70af-4106-47a2-b8dc-af395825f45a" class="wlWriterEditableSmartContent" style="padding-bottom: 0px;margin: 0px;padding-left: 0px;padding-right: 0px;float: none;padding-top: 0px">
<pre><span style="color: #0000FF">public</span><span style="color: #000000"> </span><span style="color: #0000FF">override</span><span style="color: #000000"> </span><span style="color: #0000FF">string</span><span style="color: #000000"> GetVaryByCustomString(HttpContext context, </span><span style="color: #0000FF">string</span><span style="color: #000000"> custom)
{
  </span><span style="color: #0000FF">if</span><span style="color: #000000"> (custom.Equals(</span><span style="color: #800000">"</span><span style="color: #800000">RequestVerificationTokenCookie</span><span style="color: #800000">"</span><span style="color: #000000">, StringComparison.OrdinalIgnoreCase))
  {
    </span><span style="color: #0000FF">string</span><span style="color: #000000"> verificationTokenCookieName </span><span style="color: #000000">=</span><span style="color: #000000">
      context.Request.Cookies
        .Cast</span><span style="color: #000000">&lt;</span><span style="color: #0000FF">string</span><span style="color: #000000">&gt;</span><span style="color: #000000">()
        .FirstOrDefault(cn </span><span style="color: #000000">=&gt;</span><span style="color: #000000"> cn.StartsWith(</span><span style="color: #800000">"</span><span style="color: #800000">__requestverificationtoken</span><span style="color: #800000">"</span><span style="color: #000000">, StringComparison.InvariantCultureIgnoreCase));
    </span><span style="color: #0000FF">if</span><span style="color: #000000"> (</span><span style="color: #000000">!</span><span style="color: #0000FF">string</span><span style="color: #000000">.IsNullOrEmpty(verificationTokenCookieName))
    {
      </span><span style="color: #0000FF">return</span><span style="color: #000000"> context.Request.Cookies[verificationTokenCookieName].Value;
    }
  }

  </span><span style="color: #0000FF">return</span><span style="color: #000000"> </span><span style="color: #0000FF">base</span><span style="color: #000000">.GetVaryByCustomString(context, custom);
}</span></pre>
<p><!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --></p>
</div>
<p>This code checks if the anti-forgery token cookie is sent, and in that case, returns its value that acts as the key for the cached version of the page (which can be safely used since the cookie and the hidden field should have the same partial value); otherwise the base class&#8217; method is invoked which ends up ignoring the cached versions of the page.</p>
<p>Please note that this will generate a lot of cached versions of the page (at least one by user&#8217;s session) that can greatly degrade the site performance. In this case I decided to keep the cache for that page as it was the home page and I assumed it would be hit several times by the same user.</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
