jQuery Ajax chiama e Html.AntiForgeryToken ()

Ho implementato nella mia app la mitigazione degli attacchi CSRF seguendo le informazioni che ho letto su alcuni post del blog su Internet. In particolare questi post sono stati il ​​driver della mia implementazione

  • Best practice per ASP.NET MVC dal team di contenuti per gli sviluppatori di Strumenti Web e ASP.NET
  • Anatomia di un Cross-site Request Forgery Attack dal blog di Phil Haack
  • AntiForgeryToken in ASP.NET MVC Framework – Html.AntiForgeryToken e ValidateAntiForgeryToken Attributo dal blog di David Hayden

Fondamentalmente quegli articoli e le raccomandazioni dicono che per prevenire l’attacco CSRF chiunque dovrebbe implementare il seguente codice:

1) Aggiungi il [ValidateAntiForgeryToken] ad ogni azione che accetta il verbo POST Http

 [HttpPost] [ValidateAntiForgeryToken] public ActionResult SomeAction( SomeModel model ) { } 

2) Aggiungi l’ all’interno dei moduli che inviano i dati al server

 

Ad ogni modo, in alcune parti della mia app, sto facendo dei POST Ajax con jQuery al server senza avere alcun modulo. Ciò accade ad esempio dove sto permettendo all’utente di cliccare su un’immagine per fare un’azione specifica.

Supponiamo di avere una tabella con un elenco di attività. Ho un’immagine su una colonna della tabella che dice “Segna attività come completata” e quando l’utente fa clic su quell’attività eseguo l’Ajax POST come nell’esempio seguente:

 $("a.markAsDone").click(function (event) { event.preventDefault(); $.ajax({ type: "post", dataType: "html", url: $(this).attr("rel"), data: {}, success: function (response) { // .... } }); }); 

Come posso utilizzare in questi casi? Devo includere la chiamata di aiuto all’interno del parametro dei dati della chiamata Ajax?

Scusa per il post lungo e grazie mille per l’aiuto

MODIFICA :

Come da risposta jayrdub ho usato nel modo seguente

 $("a.markAsDone").click(function (event) { event.preventDefault(); $.ajax({ type: "post", dataType: "html", url: $(this).attr("rel"), data: { AddAntiForgeryToken({}), id: parseInt($(this).attr("title")) }, success: function (response) { // .... } }); }); 

Io uso una semplice funzione js come questa

 AddAntiForgeryToken = function(data) { data.__RequestVerificationToken = $('#__AjaxAntiForgeryForm input[name=__RequestVerificationToken]').val(); return data; }; 

Poiché ogni modulo su una pagina avrà lo stesso valore per il token, basta mettere qualcosa di simile nella pagina principale più in alto

 <%-- used for ajax in AddAntiForgeryToken() --%> 
<%= Html.AntiForgeryToken()%>

Quindi nella tua chiamata ajax fai (modificato per abbinare il tuo secondo esempio)

 $.ajax({ type: "post", dataType: "html", url: $(this).attr("rel"), data: AddAntiForgeryToken({ id: parseInt($(this).attr("title")) }), success: function (response) { // .... } }); 

Mi piace la soluzione fornita da 360Airwalk, ma potrebbe essere leggermente migliorata.

Il primo problema è che se si creano $.post() con dati vuoti, jQuery non aggiunge un’intestazione Content-Type , e in questo caso ASP.NET MVC non riesce a ricevere e controllare il token. Quindi devi assicurarti che l’intestazione sia sempre lì.

Un altro miglioramento è il supporto di tutti i verbi HTTP con contenuto : POST, PUT, DELETE ecc. Sebbene tu possa utilizzare solo POST nella tua applicazione, è meglio avere una soluzione generica e verificare che tutti i dati che ricevi con qualsiasi verbo abbiano un anti-contraffazione gettone.

 $(document).ready(function () { var securityToken = $('[name=__RequestVerificationToken]').val(); $(document).ajaxSend(function (event, request, opt) { if (opt.hasContent && securityToken) { // handle all verbs with content var tokenParam = "__RequestVerificationToken=" + encodeURIComponent(securityToken); opt.data = opt.data ? [opt.data, tokenParam].join("&") : tokenParam; // ensure Content-Type header is present! if (opt.contentType !== false || event.contentType) { request.setRequestHeader( "Content-Type", opt.contentType); } } }); }); 

Non utilizzare Html.AntiForgeryToken . Utilizzare invece AntiForgery.GetTokens e AntiForgery.Validate dall’API Web come descritto in Prevenzione degli attacchi CSRF (Cross-Site Request Forgery) .

So che ci sono molte altre risposte, ma questo articolo è carino e conciso e ti obbliga a controllare tutti i tuoi HttpPost, non solo alcuni di loro:

http://richiban.wordpress.com/2013/02/06/validating-net-mvc-4-anti-forgery-tokens-in-ajax-requests/

Usa le intestazioni HTTP invece di provare a modificare la raccolta del modulo.

server

 //make sure to add this to your global action filters [AttributeUsage(AttributeTargets.Class)] public class ValidateAntiForgeryTokenOnAllPosts : AuthorizeAttribute { public override void OnAuthorization( AuthorizationContext filterContext ) { var request = filterContext.HttpContext.Request; // Only validate POSTs if (request.HttpMethod == WebRequestMethods.Http.Post) { // Ajax POSTs and normal form posts have to be treated differently when it comes // to validating the AntiForgeryToken if (request.IsAjaxRequest()) { var antiForgeryCookie = request.Cookies[AntiForgeryConfig.CookieName]; var cookieValue = antiForgeryCookie != null ? antiForgeryCookie.Value : null; AntiForgery.Validate(cookieValue, request.Headers["__RequestVerificationToken"]); } else { new ValidateAntiForgeryTokenAttribute() .OnAuthorization(filterContext); } } } } 

Cliente

 var token = $('[name=__RequestVerificationToken]').val(); var headers = {}; headers["__RequestVerificationToken"] = token; $.ajax({ type: 'POST', url: '/Home/Ajax', cache: false, headers: headers, contentType: 'application/json; charset=utf-8', data: { title: "This is my title", contents: "These are my contents" }, success: function () { ... }, error: function () { ... } }); 

Mi sento un necromante avanzato qui, ma questo è ancora un problema 4 anni dopo in MVC5.

Per gestire correttamente le richieste di ajax, il token anti-contraffazione deve essere passato al server su chiamate ajax. L’integrazione nei dati e nei modelli dei tuoi post è disordinata e inutile. Aggiungere il token come intestazione personalizzata è pulito e riutilizzabile e puoi configurarlo in modo da non doverlo ricordare ogni volta.

Esiste un’eccezione: l’ajax discreto non richiede un trattamento speciale per le chiamate ajax. Il token viene passato come al solito nel normale campo di input nascosto. Esattamente come un POST regolare.

_Layout.cshtml

In _layout.cshtml ho questo blocco JavaScript. Non scrive il token nel DOM, piuttosto usa jQuery per estrarlo dal letterale di input nascosto generato da MVC Helper. La stringa Magic che è il nome dell’intestazione è definita come una costante nella class attribute.

  

Notare l’uso di virgolette singole nella funzione beforeSend: l’elemento di input che viene reso utilizza le virgolette doppie che interrompono il letterale JavaScript.

JavaScript client

Quando viene eseguita, la funzione beforeSend precedente viene chiamata e AntiForgeryToken viene automaticamente aggiunto alle intestazioni della richiesta.

 $.ajax({ type: "POST", url: "CSRFProtectedMethod", dataType: "json", contentType: "application/json; charset=utf-8", success: function (data) { //victory } }); 

Libreria del server

È richiesto un attributo personalizzato per elaborare il token non standard. Questo si basa sulla soluzione di @ viggity, ma gestisce correttamente ajax non invadente. Questo codice può essere nascosto nella libreria comune

 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class ValidateAntiForgeryTokenOnAllPosts : AuthorizeAttribute { public const string HTTP_HEADER_NAME = "x-RequestVerificationToken"; public override void OnAuthorization(AuthorizationContext filterContext) { var request = filterContext.HttpContext.Request; // Only validate POSTs if (request.HttpMethod == WebRequestMethods.Http.Post) { var headerTokenValue = request.Headers[HTTP_HEADER_NAME]; // Ajax POSTs using jquery have a header set that defines the token. // However using unobtrusive ajax the token is still submitted normally in the form. // if the header is present then use it, else fall back to processing the form like normal if (headerTokenValue != null) { var antiForgeryCookie = request.Cookies[AntiForgeryConfig.CookieName]; var cookieValue = antiForgeryCookie != null ? antiForgeryCookie.Value : null; AntiForgery.Validate(cookieValue, headerTokenValue); } else { new ValidateAntiForgeryTokenAttribute() .OnAuthorization(filterContext); } } } } 

Server / Controller

Ora applichi l’attributo alla tua azione. Ancora meglio è ansible applicare l’attributo al controller e tutte le richieste verranno convalidate.

 [HttpPost] [ValidateAntiForgeryTokenOnAllPosts] public virtual ActionResult CSRFProtectedMethod() { return Json(true, JsonRequestBehavior.DenyGet); } 

Stavo solo implementando questo problema nel mio attuale progetto. l’ho fatto per tutti i POST ajax che necessitavano di un utente autenticato.

Prima di tutto ho deciso di agganciare le mie chiamate jquery ajax per non ripetermi troppo spesso. questo snippet javascript garantisce che tutte le chiamate ajax (post) aggiungano il token di convalida della richiesta alla richiesta. Nota: il nome __RequestVerificationToken viene utilizzato dal framework .Net in modo da poter utilizzare le funzioni standard Anti-CSRF come mostrato di seguito.

 $(document).ready(function () { var securityToken = $('[name=__RequestVerificationToken]').val(); $('body').bind('ajaxSend', function (elm, xhr, s) { if (s.type == 'POST' && typeof securityToken != 'undefined') { if (s.data.length > 0) { s.data += "&__RequestVerificationToken=" + encodeURIComponent(securityToken); } else { s.data = "__RequestVerificationToken=" + encodeURIComponent(securityToken); } } }); }); 

Nelle tue viste in cui è necessario che il token sia disponibile per il javascript sopra, usa semplicemente il comune HTML-Helper. Puoi praticamente aggiungere questo codice dove vuoi. L’ho inserito all’interno di un’istruzione if (Request.IsAuthenticated):

 @Html.AntiForgeryToken() // you can provide a string as salt when needed which needs to match the one on the controller 

Nel controller, utilizzare semplicemente il meccanismo standard ASP.Net MVC Anti-CSRF. L’ho fatto così (anche se in realtà ho usato Salt).

 [HttpPost] [Authorize] [ValidateAntiForgeryToken] public JsonResult SomeMethod(string param) { // do something return Json(true); } 

Con Firebug o uno strumento simile puoi facilmente vedere come le tue richieste POST ora hanno un parametro __RequestVerificationToken aggiunto.

Penso che tutto ciò che devi fare sia assicurare che l’input “__RequestVerificationToken” sia incluso nella richiesta POST. L’altra metà delle informazioni (ad esempio il token nel cookie dell’utente) viene già inviata automaticamente con una richiesta POST AJAX.

Per esempio,

 $("a.markAsDone").click(function (event) { event.preventDefault(); $.ajax({ type: "post", dataType: "html", url: $(this).attr("rel"), data: { "__RequestVerificationToken": $("input[name=__RequestVerificationToken]").val() }, success: function (response) { // .... } }); }); 

Oltre al mio commento contro la risposta di @ JBall che mi ha aiutato lungo la strada, questa è la risposta finale che funziona per me. Sto usando MVC e Razor e sto inviando un modulo utilizzando jQuery AJAX in modo da poter aggiornare una vista parziale con alcuni nuovi risultati e non ho voluto fare un postback completo (e sfarfallio della pagina).

Aggiungi il @Html.AntiForgeryToken() all’interno del modulo come al solito.

Il mio codice pulsante di invio AJAX (ovvero un evento onclick) è:

 //User clicks the SUBMIT button $("#btnSubmit").click(function (event) { //prevent this button submitting the form as we will do that via AJAX event.preventDefault(); //Validate the form first if (!$('#searchForm').validate().form()) { alert("Please correct the errors"); return false; } //Get the entire form's data - including the antiforgerytoken var allFormData = $("#searchForm").serialize(); // The actual POST can now take place with a validated form $.ajax({ type: "POST", async: false, url: "/Home/SearchAjax", data: allFormData, dataType: "html", success: function (data) { $('#gridView').html(data); $('#TestGrid').jqGrid('setGridParam', { url: '@Url.Action("GetDetails", "Home", Model)', datatype: "json", page: 1 }).trigger('reloadGrid'); } }); 

Ho lasciato l’azione “successo” in quanto mostra come viene aggiornata la vista parziale che contiene un MvcJqGrid e come viene aggiornato (molto potente griglia jqGrid e questo è un brillante wrapper MVC per questo).

Il mio metodo di controllo assomiglia a questo:

  //Ajax SUBMIT method [ValidateAntiForgeryToken] public ActionResult SearchAjax(EstateOutlet_D model) { return View("_Grid", model); } 

Devo ammettere di non essere un fan del POSTing dei dati di un intero modulo come un modello, ma se hai bisogno di farlo allora questo è un modo che funziona. MVC rende il binding dei dati troppo facile, quindi piuttosto che subire 16 valori individuali (o un FormCollection debolmente tipizzato) questo è OK, immagino. Se si conosce meglio per favore fatemi sapere come voglio produrre robusto codice C #.

Puoi farlo anche:

 $("a.markAsDone").click(function (event) { event.preventDefault(); $.ajax({ type: "post", dataType: "html", url: $(this).attr("rel"), data: $('
@Html.AntiForgeryToken()
').serialize(), success: function (response) { // .... } }); });

Questo sta usando Razor , ma se usi la syntax di WebForms puoi usare anche i <%= %>

1.Definisci la funzione per ottenere il token dal server

 @function { public string TokenHeaderValue() { string cookieToken, formToken; AntiForgery.GetTokens(null, out cookieToken, out formToken); return cookieToken + ":" + formToken; } } 

2. Ottenere il token e impostare l’intestazione prima di inviarlo al server

 var token = '@TokenHeaderValue()'; $http({ method: "POST", url: './MainBackend/MessageDelete', data: dataSend, headers: { 'RequestVerificationToken': token } }).success(function (data) { alert(data) }); 

3. Convalida onserver su HttpRequestBase sul metodo che gestisci Post / get

  string cookieToken = ""; string formToken = ""; string[] tokens = Request.Headers["RequestVerificationToken"].Split(':'); if (tokens.Length == 2) { cookieToken = tokens[0].Trim(); formToken = tokens[1].Trim(); } AntiForgery.Validate(cookieToken, formToken); 

trovato questa idea molto intelligente da https://gist.github.com/scottrippey/3428114 per ogni $ .ajax chiama modifica la richiesta e aggiungi il token.

 // Setup CSRF safety for AJAX: $.ajaxPrefilter(function(options, originalOptions, jqXHR) { if (options.type.toUpperCase() === "POST") { // We need to add the verificationToken to all POSTs var token = $("input[name^=__RequestVerificationToken]").first(); if (!token.length) return; var tokenName = token.attr("name"); // If the data is JSON, then we need to put the token in the QueryString: if (options.contentType.indexOf('application/json') === 0) { // Add the token to the URL, because we can't add it to the JSON data: options.url += ((options.url.indexOf("?") === -1) ? "?" : "&") + token.serialize(); } else if (typeof options.data === 'string' && options.data.indexOf(tokenName) === -1) { // Append to the data string: options.data += (options.data ? "&" : "") + token.serialize(); } } }); 

So che è passato un po ‘di tempo da quando questa domanda è stata postata, ma ho trovato una risorsa davvero utile, che parla dell’uso di AntiForgeryToken e rende meno problematico l’utilizzo. Fornisce anche il plugin jQuery per includere facilmente token antiforgery nelle chiamate AJAX:

Ricette richieste anti-contraffazione per ASP.NET MVC e AJAX

Non sto contribuendo molto, ma forse qualcuno lo troverà utile.

Leggero miglioramento della soluzione 360Airwalk. Questo configura il token anti-contraffazione all’interno della funzione javascript, quindi @ Html.AntiForgeryToken () non deve più essere incluso in ogni vista.

 $(document).ready(function () { var securityToken = $('@Html.AntiForgeryToken()').attr('value'); $('body').bind('ajaxSend', function (elm, xhr, s) { if (s.type == 'POST' && typeof securityToken != 'undefined') { if (s.data.length > 0) { s.data += "&__RequestVerificationToken=" + encodeURIComponent(securityToken); } else { s.data = "__RequestVerificationToken=" + encodeURIComponent(securityToken); } } }); }); 
 function DeletePersonel(id) { var data = new FormData(); data.append("__RequestVerificationToken", "@HtmlHelper.GetAntiForgeryToken()"); $.ajax({ type: 'POST', url: '/Personel/Delete/' + id, data: data, cache: false, processData: false, contentType: false, success: function (result) { } }); } public static class HtmlHelper { public static string GetAntiForgeryToken() { System.Text.RegularExpressions.Match value = System.Text.RegularExpressions.Regex.Match(System.Web.Helpers.AntiForgery.GetHtml().ToString(), "(?:value=\")(.*)(?:\")"); if (value.Success) { return value.Groups[1].Value; } return ""; } } 

Sto usando un post Ajax per eseguire un metodo di eliminazione (sembra essere da una timeline visjs, ma che non è relelvant). Questo è ciò che sis:

Questo è il mio Index.cshtml

 @Scripts.Render("~/bundles/schedule") @Styles.Render("~/bundles/visjs") @Html.AntiForgeryToken()  

Tutto ciò che ho aggiunto qui era @Html.AntiForgeryToken() per far apparire il token nella pagina

Poi nel mio post ajax ho usato:

 $.ajax( { type: 'POST', url: '/ScheduleWorks/Delete/' + item.id, data: { '__RequestVerificationToken': $("input[name='__RequestVerificationToken']").val() } } ); 

Che aggiunge il valore del token, raschiato fuori dalla pagina, ai campi pubblicati

Prima di questo ho provato a inserire il valore nelle intestazioni, ma ho ottenuto lo stesso errore

Sentiti libero di postare miglioramenti. Questo certamente sembra essere un approccio semplice che posso capire

AntiforgeryToken è ancora un dolore, nessuno degli esempi sopra ha funzionato parola per parola per me. Troppi per c’è. Quindi li ho combinati tutti. Hai bisogno di un @ Html.AntiforgeryToken in una forma che gira intorno a iirc

Risolto come così:

 function Forgizzle(eggs) { eggs.__RequestVerificationToken = $($("input[name=__RequestVerificationToken]")[0]).val(); return eggs; } $.ajax({ url: url, type: 'post', data: Forgizzle({ id: id, sweets: milkway }), }); 

In caso di dubbio, aggiungi altri $ segni