2009-08-28 9 views
17

Sono in procinto di aggiungere alcune funzionalità dell'interfaccia utente a un sito Web/MVC ibrido. In questo caso, aggiungo alcune funzionalità di interfaccia utente AJAX a una pagina WebForms (tramite jQuery) e i dati provengono da un MVC JsonResult. Tutto funziona al 100%, con un'eccezione:Utilizzo di MVC HtmlHelper da un WebForm

Vorrei implementare la protezione XSRF di AntiForgeryToken. L'ho usato in combinazione con l'attributo ValidateAntiForgeryToken sulle mie applicazioni MVC pure, ma vorrei sapere come implementare il metodo Html.AntiForgeryToken() in WebForms. Here's an example using a UrlHelper.

Sto avendo qualche problema a visualizzare ViewContext/RequestContext "mocked" correttamente. Come dovrei usare HtmlHelpers in una pagina WebForms?

Edit: Sto cercando di recuperare l'AntiForgeryToken dalla mia pagina WebForms, non dal MVC JsonResult.

+0

Ho lo stesso problema: una pagina WebForms legacy che deve essere caricata su un'azione MVC con un 'AntiForgeryToken'. Voglio aggiungere 'Html.AntiForgeryToken()' alla pagina WebForms senza riscriverlo in MVC. – Keith

risposta

6

Il metodo chiave è nel codice sorgente MVC: GetAntiForgeryTokenAndSetCookie

Questo crea un'istanza di una classe sigillata interno chiamato AntiForgeryData.

L'istanza è serializzata in un cookie "__RequestVerificationToken_" + una versione codificata di base 64 del percorso dell'applicazione.

La stessa istanza di AntiForgeryData viene serializzata in un input nascosto.

L'unica parte del AntiForgeryData è ottenuto con una RNGCryptoServiceProvider.GetBytes()

Tutto questo potrebbe essere falsificati in una pagina WebForms, l'unica po 'disordinato è la serializzazione della classe sigillata nascosto. Sfortunatamente il metodo chiave (GetAntiForgeryTokenAndSetCookie) si basa su ViewContext.HttpContext.Request per accedere ai cookie, mentre il Web Form deve utilizzare HttpContext.Current.Request.


Aggiornamento

Non molto collaudo e un sacco di codice, ma credo di aver incrinato questo con una piccola riflessione. Dove ho usato riflessione ho lasciato la linea equivalente commentata sopra:

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

utilizzo è quindi simile a MVC:

WebFormAntiForgery.AntiForgeryToken() 

crea lo stesso cookie e lo stesso HTML come MVC metodi.

Non mi sono preoccupato dei metodi sale e dominio, ma sarebbero stati abbastanza semplici da aggiungere.

+0

Ciao, ho appena provato a implementarlo in un webform app (.NET 4.0, MVC 3.0) e ottenuto un errore "Riferimento oggetto non impostato su un'istanza di un oggetto" da questa riga: var serializerCtor = serializerType.GetConstructor (new Type [0]); Qualcuno può aiutare? Sono un po 'fuori dalla mia profondità qui. – cjacques

+0

@BFOT questo è stato scritto su ASP.Net 2, MVC 1 ed è un trucco totale - sto riflettendo sul loro token anti-contraffazione, ma sono tutti metodi privati. Le probabilità sono che questo richiederà un po 'di aggiornamento con ogni nuova versione di ASP e MVC, scaricare la sorgente MVC 3 e vedere come sono cambiati i metodi - se siamo molto fortunati potrebbero anche averli esposti in modo che i WebForm possano usali. – Keith

+0

grazie per la risposta. Questo fine settimana darò un'occhiata all'ultima fonte MVC per vedere se riesco a risolverlo. Sentiti libero di pubblicare una versione rivista del tuo codice sopra se hai del tempo libero. ;) – cjacques

0

si potrebbe creare un nuovo HtmlHelper nel controllore e poi tirare il xrsf contro da lì:

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

È a mia conoscenza che AntiForgeryToken imposta un cookie e inietta un campo modulo nascosto, in modo che i due possano essere confrontati. Come ci riesce? –

1

Per impostazione predefinita, ASP.NET WebForms comprendono già le misure per convalidare eventi e ViewState. Phil Haack ne parla un po 'nel post collegato. XSRF strategie di mitigazione sono parlato here (Scott Hanselman) e here (Dino Esposito)

+1

Ottimi collegamenti, grazie. Speravo in un modo per utilizzare AntiForgeryToken in modo specifico, perché il "consumatore" del servizio Web MVC si trova su una pagina WebForms (che non sto riscrivendo in MVC). –

17

So che questa è una vecchia domanda, ma oggi mi sono imbattuto in questo problema e ho pensato di condividere. Sto lavorando in MVC4 e ho un controllo webform (.ascx) condiviso tra MVC (tramite RenderPartial) e WebForms. In quel controllo, avevo bisogno di un token anti-contraffazione. Fortunatamente, c'è un aiuto ora è possibile utilizzare per le webforms ora che è semplice come questo:

<%= AntiForgery.GetHtml() %> 

Questo renderà il vostro segno anti-contraffazione, come si potrebbe avere in MVC.

Here's the MS link to it.

+0

Eccellente, questo è un modo migliore per risolvere il problema in WebForms 2/MVC 3 e versioni successive. – Keith

Problemi correlati