2009-08-28 18 views
17

Estoy en el proceso de agregar alguna funcionalidad de UI a un sitio híbrido de WebForms/MVC. En este caso, estoy agregando algunas características AJAX UI a una página de WebForms (a través de jQuery), y los datos provienen de un MVC JsonResult. Todo funciona al 100%, con una excepción:Uso de MVC HtmlHelper desde un WebForm

Me gustaría implementar la protección XSRF de AntiForgeryToken. Lo he usado en combinación con el atributo ValidateAntiForgeryToken en mis aplicaciones MVC puros, pero me gustaría saber cómo implementar el método Html.AntiForgeryToken() en WebForms. Here's an example using a UrlHelper.

Tengo algunos problemas para obtener ViewContext/RequestContext "burlado" correctamente. ¿Cómo debo usar HtmlHelpers en una página de WebForms?

Editar: estoy buscando para recuperar el AntiForgeryToken de mi página Web Forms, no de la MVC JsonResult.

+0

Tengo el mismo problema: una página heredada de WebForms que necesita publicar en una acción de MVC con un 'AntiForgeryToken'. Quiero agregar 'Html.AntiForgeryToken()' a la página WebForms sin reescribirla en MVC. – Keith

Respuesta

6

El método clave está en el código fuente de MVC: GetAntiForgeryTokenAndSetCookie

Esto crea una instancia de una clase sellada interna llamada AntiForgeryData.

La instancia se serializa en una cookie "__RequestVerificationToken_" + una versión codificada en base 64 de la ruta de la aplicación.

La misma instancia de AntiForgeryData se serializa en una entrada oculta.

La única parte de la AntiForgeryData se consigue con un RNGCryptoServiceProvider.GetBytes()

Todo esto podría ser falsa en una página Web Forms, el único poco desordenado es la serialización de la clase sellada oculta. Lamentablemente, el método clave (GetAntiForgeryTokenAndSetCookie) se basa en ViewContext.HttpContext.Request para obtener las cookies, mientras que WebForm necesita usar HttpContext.Current.Request.


actualización

No mucho ensayo y una gran cantidad de código, pero creo que he agrietado esto con un poco de reflexión. Cuando he utilizado la reflexión que he salido de la línea equivalente comentado anteriormente:

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; 
    } 
} 

uso es entonces similar a la MVC:

WebFormAntiForgery.AntiForgeryToken() 

Se crea la misma cookie y la misma HTML como el MVC métodos.

No me he molestado con los métodos de sal y dominio, pero serían bastante fáciles de agregar.

+0

Hola, intenté implementar esto en un webforms aplicación (.NET 4.0, MVC 3.0) y obtuvo el error "Referencia de objeto no configurado a una instancia de un objeto" de esta línea: var serializerCtor = serializerType.GetConstructor (new Type [0]); ¿Alguien puede ayudar? Estoy un poco fuera de mi profundidad aquí. – cjacques

+0

@BFOT esto fue escrito en ASP.Net 2, MVC 1 y es un truco total. Estoy reflexionando sobre sus elementos de token anti falsificación, pero son todos métodos privados. Lo más probable es que esto requiera una pequeña actualización con cada nueva versión de ASP y MVC, descargue la fuente MVC 3 y vea cómo han cambiado los métodos. Si tenemos mucha suerte, incluso podrían haberlos expuesto para que WebForms pueda usalos, usalos a ellos. – Keith

+0

gracias por la respuesta. Voy a echar un vistazo a la última fuente de MVC este fin de semana para ver si puedo resolverlo. Por favor, siéntase libre de publicar una versión revisada de su código anterior si tiene algo de tiempo libre. ;) – cjacques

0

se podría crear una nueva HtmlHelper en su controlador y luego tirar de la lucha contra xrsf desde allí:

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); 
+0

Entiendo que AntiForgeryToken establece una cookie e inyecta un campo de formulario oculto, por lo que se pueden comparar. ¿Cómo logra esto? –

1

De manera predeterminada, ASP.NET Web Forms ya incluyen medidas para validar eventos y estado de vista. Phil Haack habla un poco acerca de eso en la publicación vinculada. estrategias de mitigación XSRF se habla de here (Scott Hanselman) y here (Dino Esposito)

+1

Muy buenos enlaces, gracias. Esperaba una forma de utilizar AntiForgeryToken específicamente, porque el "consumidor" del servicio web MVC está en una página de WebForms (que no voy a volver a escribir en MVC). –

17

Sé que esta es una vieja pregunta, pero me encontré con este problema hoy y pensé que iba a compartir. Estoy trabajando en MVC4 y tengo un control de formulario web (.ascx) que se comparte entre MVC (a través de RenderPartial) y WebForms. En ese control, necesitaba una ficha antiforgery. Afortunadamente, hay un ayudante ahora se puede utilizar en sus formularios web ahora que es tan simple como esto:

<%= AntiForgery.GetHtml() %> 

Esto hará que el token de anti-falsificación como puede conseguir en MVC.

Here's the MS link to it.

+0

Excelente, esta es una mejor manera de resolver el problema en WebForms 2/MVC 3 y superior. – Keith

Cuestiones relacionadas