Using an MVC HtmlHelper from a WebForm

余生长醉 提交于 2019-11-27 07:28:07

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.

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.

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)

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);
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!