I\'m in the process of adding some UI functionality to a hybrid WebForms/MVC site. In this case, I\'m adding some AJAX UI features to a WebForms page (via jQuery), and the
The key method is in the MVC source code: GetAntiForgeryTokenAndSetCookie
This creates an instance of an internal sealed class called AntiForgeryData
.
The instance is serialised into a cookie "__RequestVerificationToken_" + a base 64 encoded version of the application path.
The same instance of AntiForgeryData
is serialised into a hidden input.
The unique part of the AntiForgeryData
is got with an RNGCryptoServiceProvider.GetBytes()
All of this could be spoofed in a WebForms page, the only messy bit is the serialisation of the hidden sealed class. Unfortunately the key method (GetAntiForgeryTokenAndSetCookie
) relies on ViewContext.HttpContext.Request
to get at the cookies, while the WebForm needs to use HttpContext.Current.Request
instead.
Update
Not much testing and a lot of code, but I think I've cracked this with a little reflection. Where I've used reflection I've left the equivalent line commented out above:
using System;
using System.Reflection;
using System.Web;
using System.Web.Mvc;
/// <summary>Utility to provide MVC anti forgery tokens in WebForms pages</summary>
public class WebFormAntiForgery
{
/// <summary>Create an anti forgery token in a WebForms page</summary>
/// <returns>The HTML input and sets the cookie</returns>
public static string AntiForgeryToken()
{
string formValue = GetAntiForgeryTokenAndSetCookie();
// string fieldName = AntiForgeryData.GetAntiForgeryTokenName(null);
var mvcAssembly = typeof(HtmlHelper).Assembly;
var afdType = mvcAssembly.GetType("System.Web.Mvc.AntiForgeryData");
string fieldName = Convert.ToString(afdType.InvokeMember(
"GetAntiForgeryTokenName",
BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.InvokeMethod,
null,
null,
new object[] { null }));
TagBuilder builder = new TagBuilder("input");
builder.Attributes["type"] = "hidden";
builder.Attributes["name"] = fieldName;
builder.Attributes["value"] = formValue;
return builder.ToString(TagRenderMode.SelfClosing);
}
static string GetAntiForgeryTokenAndSetCookie()
{
var mvcAssembly = typeof(HtmlHelper).Assembly;
var afdType = mvcAssembly.GetType("System.Web.Mvc.AntiForgeryData");
// new AntiForgeryDataSerializer();
var serializerType = mvcAssembly.GetType("System.Web.Mvc.AntiForgeryDataSerializer");
var serializerCtor = serializerType.GetConstructor(new Type[0]);
object serializer = serializerCtor.Invoke(new object[0]);
// string cookieName = AntiForgeryData.GetAntiForgeryTokenName(HttpContext.Current.Request.ApplicationPath);
string cookieName = Convert.ToString(afdType.InvokeMember(
"GetAntiForgeryTokenName",
BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.InvokeMethod,
null,
null,
new object[] { HttpContext.Current.Request.ApplicationPath }));
// AntiForgeryData cookieToken;
object cookieToken;
HttpCookie cookie = HttpContext.Current.Request.Cookies[cookieName];
if (cookie != null)
{
// cookieToken = Serializer.Deserialize(cookie.Value);
cookieToken = serializerType.InvokeMember("Deserialize", BindingFlags.InvokeMethod, null, serializer, new object[] { cookie.Value });
}
else
{
// cookieToken = AntiForgeryData.NewToken();
cookieToken = afdType.InvokeMember(
"NewToken",
BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod,
null,
null,
new object[0]);
// string cookieValue = Serializer.Serialize(cookieToken);
string cookieValue = Convert.ToString(serializerType.InvokeMember("Serialize", BindingFlags.InvokeMethod, null, serializer, new object[] { cookieToken }));
var newCookie = new HttpCookie(cookieName, cookieValue) { HttpOnly = true };
HttpContext.Current.Response.Cookies.Set(newCookie);
}
// AntiForgeryData formToken = new AntiForgeryData(cookieToken)
// {
// CreationDate = DateTime.Now,
// Salt = salt
// };
var ctor = afdType.GetConstructor(new Type[] { afdType });
object formToken = ctor.Invoke(new object[] { cookieToken });
afdType.InvokeMember("CreationDate", BindingFlags.SetProperty, null, formToken, new object[] { DateTime.Now });
afdType.InvokeMember("Salt", BindingFlags.SetProperty, null, formToken, new object[] { null });
// string formValue = Serializer.Serialize(formToken);
string formValue = Convert.ToString(serializerType.InvokeMember("Serialize", BindingFlags.InvokeMethod, null, serializer, new object[] { formToken }));
return formValue;
}
}
Usage is then similar to MVC:
WebFormAntiForgery.AntiForgeryToken()
It creates the same cookie and the same HTML as the MVC methods.
I haven't bothered with the salt and domain methods, but they would be fairly easy to add in.
By default, ASP.NET WebForms already include measures to validate events and viewstate. Phil Haack talks a little bit about that in the linked post. XSRF mitigation strategies are talked about here (Scott Hanselman) and here (Dino Esposito)
I know this is an old question, but I ran into this issue today and thought I'd share. I'm working in MVC4 and have a webform control (.ascx) that's shared between both MVC (via RenderPartial) and WebForms. In that control, I needed an antiforgery token. Fortunately, there's a helper now you can use in your webforms now that's as simple as this:
<%= AntiForgery.GetHtml() %>
This will render your anti-forgery token like you would get in MVC.
Here's the MS link to it.
you could create a new HtmlHelper in your controller and then pull the anti xrsf from there:
var htmlHelper = new HtmlHelper(
new ViewContext(
ControllerContext,
new WebFormView("omg"),
new ViewDataDictionary(),
new TempDataDictionary()),
new ViewPage());
var xsrf = htmlHeler.AntiForgeryToken;
myObject.XsrfToken = xsrf;
return JsonResult(myObject);