jQuery Ajax ruft auf und das Html.AntiForgeryToken ()

207

Ich habe in meiner App die Abwehr von CSRF-Angriffen implementiert, nachdem ich Informationen in einem Blogbeitrag im Internet gelesen habe. Insbesondere dieser Beitrag war der Treiber meiner Implementierung

Grundsätzlich heißt es in diesen Artikeln und Empfehlungen, dass jeder den folgenden Code implementieren sollte, um den CSRF-Angriff zu verhindern:

1) Fügen Sie [ValidateAntiForgeryToken]bei jeder Aktion, die das POST-HTTP-Verb akzeptiert, das hinzu

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

2) Fügen Sie den <%= Html.AntiForgeryToken() %>Helfer in Formulare ein, die Daten an den Server senden

<div style="text-align:right; padding: 8px;">
    <%= Html.AntiForgeryToken() %>
    <input type="submit" id="btnSave" value="Save" />
</div>

Wie auch immer, in einigen Teilen meiner App mache ich Ajax-POSTs mit jQuery auf dem Server, ohne überhaupt ein Formular zu haben. Dies geschieht zum Beispiel, wenn ich den Benutzer auf ein Bild klicken lasse, um eine bestimmte Aktion auszuführen.

Angenommen, ich habe eine Tabelle mit einer Liste von Aktivitäten. Ich habe ein Bild in einer Spalte der Tabelle mit der Aufschrift "Aktivität als abgeschlossen markieren". Wenn der Benutzer auf diese Aktivität klickt, führe ich den Ajax-POST wie im folgenden Beispiel aus:

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

Wie kann ich das <%= Html.AntiForgeryToken() %>in diesen Fällen verwenden? Sollte ich den Hilfsaufruf in den Datenparameter des Ajax-Aufrufs aufnehmen?

Entschuldigung für den langen Beitrag und vielen Dank für Ihre Hilfe

EDIT :

Wie pro jayrdub Antwort habe ich in der folgenden Art und Weise verwendet ,

$("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) {
            // ....
        }
    });
});
Lorenzo
quelle
Der David Hayden Link jetzt 404s, es scheint, dass er sein Blog auf ein neues CMS migriert hat, aber nicht alle alten Inhalte über migriert hat.

Antworten:

252

Ich benutze eine einfache js-Funktion wie diese

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

Da jedes Formular auf einer Seite den gleichen Wert für das Token hat, fügen Sie einfach so etwas in Ihre oberste Masterseite ein

<%-- used for ajax in AddAntiForgeryToken() --%>
<form id="__AjaxAntiForgeryForm" action="#" method="post"><%= Html.AntiForgeryToken()%></form>  

Dann in Ihrem Ajax-Aufruf tun (bearbeitet, um Ihrem zweiten Beispiel zu entsprechen)

$.ajax({
    type: "post",
    dataType: "html",
    url: $(this).attr("rel"),
    data: AddAntiForgeryToken({ id: parseInt($(this).attr("title")) }),
    success: function (response) {
        // ....
    }
});
JeremyWeir
quelle
6
Schön, ich mag die Kapselung des Token-Abrufs.
Jball
2
@ Lorenzo, setzen Sie Ihre benutzerdefinierten Daten in den Anruf ein AddAntiForgeryToken, wie data: AddAntiForgeryToken({ id: parseInt($(this).attr("title")) }),
folgt
3
Wie schlecht wäre es, eine Idee zu verwenden ajaxSendoder zu überschreiben, ajaxum immer datamit dem Anti-Fälschungs-Token zu ergänzen ? Fügen Sie möglicherweise eine Überprüfung hinzu, um sicherzustellen, dass die urlfür Ihren Server bestimmt ist.
ta.speot.is
1
Seien Sie vorsichtig, wenn Sie den Ausgabecache verwenden.
Barbaros Alp
1
@SouhaiebBesbes Das Validierungstoken sollte für einen Benutzer auf allen Seiten gleich sein (es funktioniert in Verbindung mit einem Cookie, das gesetzt ist und gleich bleibt). Es spielt also keine Rolle, ob es mehrere Anforderungen pro Seite gibt, es ist dasselbe, wenn die Basisseite trotzdem neu geladen wird.
JeremyWeir
29

Ich mag die von 360Airwalk bereitgestellte Lösung, aber sie kann etwas verbessert werden.

Das erste Problem besteht darin, dass $.post()jQuery keinen Content-TypeHeader hinzufügt , wenn Sie mit leeren Daten arbeiten. In diesem Fall kann ASP.NET MVC das Token nicht empfangen und überprüfen. Sie müssen also sicherstellen, dass der Header immer vorhanden ist.

Eine weitere Verbesserung ist die Unterstützung aller HTTP-Verben mit Inhalten : POST, PUT, DELETE usw. Obwohl Sie in Ihrer Anwendung möglicherweise nur POSTs verwenden, ist es besser, eine generische Lösung zu haben und zu überprüfen, ob alle Daten, die Sie mit einem Verb erhalten, eine Fälschungsbekämpfung aufweisen Zeichen.

$(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);
            }
        }
    });
});
Bronx
quelle
1
+1 Sie haben Recht, ich habe nicht an das Problem mit dem leeren Post-Call gedacht. danke für die eingabe. Sie hatten Recht damit, dass wir Delete / Put noch nicht in unserem Projekt verwenden.
360Airwalk
2
+1, damit ich nicht die Funktion zu allen jQuery.Ajax-Aufrufen hinzufügen muss
Dragos Durlut
2
+1 Nur als Hinweis für die Nachwelt heißt es in der jQuery-Dokumentation für .ajaxSend()"Ab jQuery 1.8 sollte die .ajaxSend () -Methode nur an das Dokument angehängt werden." api.jquery.com/ajaxsend
RJ Cuthbertson
1
@Bronx Woher kommt das options, was in der Schlusserklärung aufgeführt ist if? Vielen Dank.
Hvaughan3
Verwenden Sie diese Option nicht, wenn eine Seite mehrere Formulare enthält. Sie müssen den Wert in beforeSend mit einem spezifischeren Selektoraufruf anstelle des Dokuments festlegen.
Dan
22

Ich weiß, dass es viele andere Antworten gibt, aber dieser Artikel ist nett und prägnant und zwingt Sie, alle Ihre HttpPosts zu überprüfen, nicht nur einige davon:

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

Es werden HTTP-Header verwendet, anstatt zu versuchen, die Formularsammlung zu ändern.

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

Klient

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 () {
        ...
    }
});
Viggity
quelle
4
Das Attribut aus dem Artikel, den Sie ebenfalls in Verbindung mit der Antwort von Bronx verlinkt haben, ist die ultimative DRY-Lösung für dieses Problem.
TugboatCaptain
2
Toller Fund. Ich habe Ihre Antwort so bearbeitet, dass sie die Codefragmente enthält, sodass die Antwort für sich allein steht, aber ich hoffe, dass die Leute auch den Rest des Artikels lesen werden. Dies scheint eine sehr saubere Lösung zu sein.
Tim Medora
danke Tim, es ist eine ausgezeichnete Idee, es ist frustrierend, wenn ein Link tot ist und die Antwort wertlos wird. Ich habe angefangen, dies bei all meinen neuen Antworten zu tun.
Viggity
Ist das MVC, WebAPI oder .NetCore? Ich kann nicht die richtigen Namespaces für WebAPI 5
Myster
20

Ich fühle mich hier wie ein fortgeschrittener Nekromant, aber dies ist 4 Jahre später in MVC5 immer noch ein Problem.

Um Ajax-Anforderungen ordnungsgemäß zu verarbeiten, muss das Anti-Fälschungs-Token bei Ajax-Aufrufen an den Server übergeben werden. Die Integration in Ihre Post-Daten und -Modelle ist chaotisch und unnötig. Das Hinzufügen des Tokens als benutzerdefinierten Header ist sauber und wiederverwendbar - und Sie können es so konfigurieren, dass Sie nicht jedes Mal daran denken müssen, es zu tun.

Es gibt eine Ausnahme: Unauffälliger Ajax benötigt keine spezielle Behandlung für Ajax-Anrufe. Das Token wird wie gewohnt im regulären ausgeblendeten Eingabefeld übergeben. Genau das gleiche wie bei einem normalen POST.

_Layout.cshtml

In _layout.cshtml habe ich diesen JavaScript-Block. Das Token wird nicht in das DOM geschrieben, sondern mithilfe von jQuery aus dem vom MVC-Helper generierten verborgenen Eingabeliteral extrahiert. Die Magic-Zeichenfolge, die der Headername ist, wird in der Attributklasse als Konstante definiert.

<script type="text/javascript">
    $(document).ready(function () {
        var isAbsoluteURI = new RegExp('^(?:[a-z]+:)?//', 'i');
        //http://stackoverflow.com/questions/10687099/how-to-test-if-a-url-string-is-absolute-or-relative

        $.ajaxSetup({
            beforeSend: function (xhr) {
                if (!isAbsoluteURI.test(this.url)) {
                    //only add header to relative URLs
                    xhr.setRequestHeader(
                       '@.ValidateAntiForgeryTokenOnAllPosts.HTTP_HEADER_NAME', 
                       $('@Html.AntiForgeryToken()').val()
                    );
                }
            }
        });
    });
</script>

Beachten Sie die Verwendung von einfachen Anführungszeichen in der Funktion beforeSend. Das gerenderte Eingabeelement verwendet doppelte Anführungszeichen, die das JavaScript-Literal beschädigen würden.

Client JavaScript

Wenn dies ausgeführt wird, wird die oben genannte Funktion beforeSend aufgerufen und das AntiForgeryToken wird automatisch zu den Anforderungsheadern hinzugefügt.

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

Serverbibliothek

Ein benutzerdefiniertes Attribut ist erforderlich, um das nicht standardmäßige Token zu verarbeiten. Dies baut auf der Lösung von @ viggity auf, behandelt jedoch unauffälligen Ajax korrekt. Dieser Code kann in Ihrer gemeinsamen Bibliothek versteckt werden

[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

Jetzt wenden Sie das Attribut einfach auf Ihre Aktion an. Noch besser können Sie das Attribut auf Ihren Controller anwenden und alle Anforderungen werden validiert.

[HttpPost]
[ValidateAntiForgeryTokenOnAllPosts]
public virtual ActionResult CSRFProtectedMethod()
{
  return Json(true, JsonRequestBehavior.DenyGet);
}
Will D.
quelle
Perfekte Lösung, viel zentraler. Vielen Dank
David Freire
Können Sie genauer erklären, warum Sie nur den Header für relative URLs hinzufügen möchten? Das ging mir über den Kopf. Tolle Lösung!
MattM
relative stellt sicher, dass der Header nur für Anforderungen festgelegt wird, die an Ihren eigenen Server zurückgehen, da das Ajax-Setup alle mit jquery gestellten Anforderungen abdeckt. Wir möchten nicht, dass das Token für jsonp- oder CORS-Anforderungen gesendet wird. Dies gilt möglicherweise auch für absolute URLs, aber es wird garantiert, dass die relative Domain dieselbe Domain ist.
Will D
1
@ WillD Ich mochte Ihre Lösung, musste sie aber etwas ändern. Da Sie $.ajaxSetupeinen allgemeinen beforesendEventhandler definieren , kann es vorkommen, dass Sie ihn überschreiben. Ich habe eine andere Lösung gefunden, bei der Sie einen zweiten Handler hinzufügen können, der auch aufgerufen wird. Funktioniert gut und unterbricht Ihre Implementierung nicht.
Viper
Hat jemand eine ASP.net 5-Version des Kunden, die das AntiForgery-Attribut validiert? Diese Version wird in der neuesten Version nicht kompiliert!
Rob McCabe
19

Verwenden Sie nicht Html.AntiForgeryToken . Verwenden Sie stattdessen AntiForgery.GetTokens und AntiForgery.Validate über die Web-API, wie unter Verhindern von CSRF-Angriffen (Cross-Site Request Forgery) in der ASP.NET MVC-Anwendung beschrieben .

Edward Brey
quelle
Für Controller-Aktionsmethoden, bei denen ein Modell einen Servermodelltyp an den bereitgestellten AJAX-JSON bindet, ist der Inhaltstyp "application / json" erforderlich, damit der richtige Modellordner verwendet werden kann. Leider schließt dies die Verwendung von Formulardaten aus, die für das Attribut [ValidateAntiForgeryToken] erforderlich sind. Daher ist Ihre Methode die einzige Möglichkeit, die ich finden kann, damit sie funktioniert. Meine einzige Frage ist, ob es immer noch in einer Webfarm oder in mehreren Azure-Webrolleninstanzen funktioniert. Wissen Sie @Edward oder jemand anderes, ob dies ein Problem ist?
Richard B
@ Edward Brey Kannst du bitte näher erläutern, warum wir es nicht verwenden sollten?
Odys
4
@Odys: An Html.AntiForgeryToken ist nichts von Natur aus falsch, aber es hat Nachteile: erfordert ein Formular, erfordert jQuery und setzt undokumentierte Implementierungsdetails für Html.AntiForgeryToken voraus. Trotzdem ist es in vielen Zusammenhängen in Ordnung. Meine Aussage "Verwenden Sie kein Html.AntiForgeryToken" ist wahrscheinlich zu stark. Meine Bedeutung ist, dass es nicht für die Verwendung mit der Web-API vorgesehen ist, wohingegen das flexiblere AntiForgery.GetTokens ist.
Edward Brey
Vielen Dank! Ich musste es ein wenig ändern, damit es für einen MVC5-Controller funktioniert, aber das war die Lösung
jao
3
Es erfordert sicherlich kein Formular. Sie müssen nur das DOM nach Namen analysieren. Mit jquery kann ich es über data {__RequestVerificationToken: $ ("input [name = __ RequestVerificationToken]") in mein Datenobjekt einfügen. Val ()}
Anthony Mason
16

Ich habe gerade dieses eigentliche Problem in meinem aktuellen Projekt implementiert. Ich habe es für alle Ajax-POSTs gemacht, die einen authentifizierten Benutzer brauchten.

Zuerst habe ich mich entschlossen, meine jquery ajax-Anrufe einzuhaken, damit ich mich nicht zu oft wiederhole. Dieses Javascript-Snippet stellt sicher, dass alle Ajax- (Post-) Aufrufe mein Anforderungsvalidierungstoken zur Anforderung hinzufügen. Hinweis: Der Name __RequestVerificationToken wird vom .Net-Framework verwendet, damit ich die unten gezeigten Standard-Anti-CSRF-Funktionen verwenden kann.

$(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);
            }
        }
    });
});

Verwenden Sie in Ihren Ansichten, in denen das Token für das oben genannte Javascript verfügbar sein soll, einfach den allgemeinen HTML-Helper. Sie können diesen Code grundsätzlich hinzufügen, wo immer Sie möchten. Ich habe es in eine if-Anweisung (Request.IsAuthenticated) eingefügt:

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

Verwenden Sie in Ihrem Controller einfach den Standard-ASP.Net MVC Anti-CSRF-Mechanismus. Ich habe es so gemacht (obwohl ich tatsächlich Salt verwendet habe).

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

Mit Firebug oder einem ähnlichen Tool können Sie leicht sehen, wie an Ihre POST-Anforderungen jetzt ein __RequestVerificationToken-Parameter angehängt ist.

360Airwalk
quelle
15

Ich denke, alles, was Sie tun müssen, ist sicherzustellen, dass die Eingabe "__RequestVerificationToken" in der POST-Anforderung enthalten ist. Die andere Hälfte der Informationen (dh das Token im Cookie des Benutzers) wird bereits automatisch mit einer AJAX POST-Anforderung gesendet.

Z.B,

$("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) {
            // ....
        }
    });
});
jball
quelle
1
Nach vielen Stunden des Experimentierens mit jQuery AJAX-Postings auf einer MVC (Razor) -Seite war dies die einfachste Antwort von allem, was für mich funktioniert hat. Fügen Sie nach dem Token einfach Ihre eigenen Datenfelder (oder das viewModel, nehme ich an) als neues Datenelement ein (jedoch innerhalb des ursprünglichen Datenobjekts).
Ralph Bacon
Wie würde ich dies implementieren, wenn sich die AJAX-Funktion auf einer HTML-Seite und nicht auf einer Razor-Seite befindet?
Bob der Baumeister
Wenn auf Ihrer HTML-Seite kein Server bereitgestellt wird AntiForgeryToken, ist sowieso alles umstritten. Wenn dies der Fall ist (nicht sicher, wie Sie in diesem Fall einen bekommen, aber vorausgesetzt, Sie sind es), würde das oben genannte gut funktionieren. Wenn Sie versuchen, eine einfache Webseite zu erstellen, die eine Anfrage an einen Server sendet, der das Token erwartet, und der Server diese Seite nicht generiert hat, haben Sie kein Glück. Das ist im Wesentlichen der Punkt des AntiForgeryToken ...
jball
6

Sie können dies auch tun:

$("a.markAsDone").click(function (event) {
    event.preventDefault();

    $.ajax({
        type: "post",
        dataType: "html",
        url: $(this).attr("rel"),
        data: $('<form>@Html.AntiForgeryToken()</form>').serialize(),
        success: function (response) {
        // ....
        }
    });
});

Dies wird verwendet Razor, aber wenn Sie WebFormsSyntax verwenden, können Sie genauso gut <%= %>Tags verwenden

Leonardo Garcia Crespo
quelle
4

Nach meinem Kommentar gegen die Antwort von @ JBall, der mir dabei geholfen hat, ist dies die endgültige Antwort, die für mich funktioniert. Ich verwende MVC und Razor und sende ein Formular mit jQuery AJAX, damit ich eine Teilansicht mit einigen neuen Ergebnissen aktualisieren kann und kein vollständiges Postback (und Seitenflimmern) durchführen möchte.

Fügen Sie das @Html.AntiForgeryToken()Formular wie gewohnt hinzu.

Mein AJAX-Übermittlungsschaltflächencode (dh ein Onclick-Ereignis) lautet:

//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');
    }
});

Ich habe die Aktion "Erfolg" beibehalten, da sie zeigt, wie die Teilansicht aktualisiert wird, die ein MvcJqGrid enthält, und wie sie aktualisiert wird (sehr leistungsfähiges jqGrid-Raster und dies ist ein brillanter MVC-Wrapper dafür).

Meine Controller-Methode sieht folgendermaßen aus:

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

Ich muss zugeben, dass ich kein Fan davon bin, die Daten eines gesamten Formulars als Modell zu veröffentlichen, aber wenn Sie dies tun müssen, ist dies eine Möglichkeit, die funktioniert. MVC macht die Datenbindung einfach zu einfach. Anstatt 16 einzelne Werte (oder eine schwach typisierte FormCollection) einzugeben, ist dies in Ordnung, denke ich. Wenn Sie es besser wissen, lassen Sie es mich bitte wissen, da ich robusten MVC C # -Code erstellen möchte.

Ralph Bacon
quelle
4

fand diese sehr clevere Idee von https://gist.github.com/scottrippey/3428114 für jeden $ .ajax-Aufruf, der die Anfrage ändert und das Token hinzufügt.

// 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();
        }
    }
});
Masterlopau
quelle
Ich habe einige der anderen oben genannten Alternativen ausprobiert. Dies hat es für mich gelöst.
HostMyBus
Ich musste jedoch hinzufügen if (options.contentType != false && options.contentType.indexOf('application/json') === 0) {, um Ajax-Anrufe
abzufangen
3

1.Definieren Sie die Funktion, um das Token vom Server abzurufen

@function
{

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

2. Holen Sie sich das Token und setzen Sie den Header, bevor Sie es an den Server senden

var token = '@TokenHeaderValue()';    

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

3. Onserver-Validierung in HttpRequestBase für die Methode, die Sie für Post / get verwenden

        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);
Tonman Neverwalk alleine
quelle
1

Ich bin mir bewusst, dass es einige Zeit her ist, seit diese Frage veröffentlicht wurde, aber ich habe eine wirklich nützliche Ressource gefunden, die die Verwendung von AntiForgeryToken beschreibt und die Verwendung weniger problematisch macht. Es bietet auch ein JQuery-Plugin, mit dem Antiforgery-Token einfach in AJAX-Aufrufe aufgenommen werden können:

Anti-Fälschungs-Anforderungsrezepte für ASP.NET MVC und AJAX

Ich trage nicht viel bei, aber vielleicht findet es jemand nützlich.

Slawek
quelle
Dieser Beitrag ist wie eine Meile lang! Ich bin sicher, es ist großartig, aber tl; dr
BritishDeveloper
1
Schade, denn es deckt das Thema gut ab. Es zeigt Ihnen nicht nur, wie Sie die Funktion verwenden, sondern erklärt auch, welches Problem dadurch behoben wird, und gibt Ihnen einen Kontext, in dem Sie verstehen, wie Sie sie richtig verwenden. Wenn es um Sicherheit geht, denke ich, dass ein tiefes Verständnis wichtig ist.
Slawek
2
Wenn es wichtig ist, sollte es so geschrieben sein, dass die Leute es lesen können;)
BritishDeveloper
1

Verwenden Sie zuerst @ Html.AntiForgeryToken () in HTML

 $.ajax({
        url: "@Url.Action("SomeMethod", "SomeController")",
        type: 'POST',
        data: JSON.stringify(jsonObject),
        contentType: 'application/json; charset=utf-8',
        dataType: 'json',
        async: false,
        beforeSend: function (request) {
            request.setRequestHeader("RequestVerificationToken", $("[name='__RequestVerificationToken']").val());
        },
        success: function (msg) {
            alert(msg);
        }
Amir Reza
quelle
1

Hier ist der einfachste Weg, den ich gesehen habe. Hinweis: Stellen Sie sicher, dass Sie "@ Html.AntiForgeryToken ()" in Ihrer Ansicht haben

  $("a.markAsDone").click(function (event) {
        event.preventDefault();
        var sToken = document.getElementsByName("__RequestVerificationToken")[0].value;
        $.ajax({
            url: $(this).attr("rel"),
            type: "POST",
            contentType: "application/x-www-form-urlencoded",
            data: { '__RequestVerificationToken': sToken, 'id': parseInt($(this).attr("title")) }
        })
        .done(function (data) {
            //Process MVC Data here
        })
        .fail(function (jqXHR, textStatus, errorThrown) {
            //Process Failure here
        });
    });
Dominic Sputo
quelle
0

Leichte Verbesserung der 360Airwalk-Lösung. Dadurch wird das Anti-Fälschungs-Token in die Javascript-Funktion eingebettet, sodass @ Html.AntiForgeryToken () nicht mehr in jeder Ansicht enthalten sein muss.

$(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);
            }
        }
    });
});
Barry MSIH
quelle
0
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 "";
    }
}
ismail eski
quelle
0

Ich verwende einen Ajax-Beitrag, um eine Löschmethode auszuführen (stammt zufällig aus einer visjs-Zeitleiste, ist aber nicht relevant). Das ist was ich sis:

Dies ist meine Index.cshtml

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

<!-- div to attach schedule to -->
<div id='schedule'></div>

<!-- div to attach popups to -->
<div id='dialog-popup'></div>

Alles, was ich hier hinzugefügt habe, war @Html.AntiForgeryToken(), das Token auf der Seite erscheinen zu lassen

Dann habe ich in meinem Ajax-Post verwendet:

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

Dadurch wird der von der Seite abgekratzte Token-Wert zu den veröffentlichten Feldern hinzugefügt

Vorher habe ich versucht, den Wert in die Header einzufügen, aber ich habe den gleichen Fehler erhalten

Fühlen Sie sich frei, Verbesserungen zu posten. Dies scheint sicherlich ein einfacher Ansatz zu sein, den ich verstehen kann

Nick.McDermaid
quelle
0

Okay, viele Beiträge hier, keiner von ihnen hat mir geholfen, Tage und Tage von Google, und immer noch nicht weiter, dass ich die ganze App von Grund auf neu geschrieben habe, und dann habe ich dieses kleine Nugget in meiner Web.confg bemerkt

 <httpCookies requireSSL="false" domain="*.localLookup.net"/>

Jetzt weiß ich nicht, warum ich es hinzugefügt habe, aber ich habe seitdem bemerkt, dass es im Debug-Modus und nicht im Produktionsmodus ignoriert wird (IE wird irgendwo auf IIS installiert).

Für mich war die Lösung eine von zwei Optionen, da ich mich nicht erinnere, warum ich sie hinzugefügt habe. Ich kann nicht sicher sein, dass andere Dinge nicht davon abhängen, und zweitens muss der Domain-Name nur in Kleinbuchstaben geschrieben sein und eine TLD muss nicht so sein wie ich in * .localLookup.net

Vielleicht hilft es vielleicht nicht. Ich hoffe es hilft jemandem

Aufgeregt
quelle
0

Die Lösung, die ich gefunden habe, ist nicht für ASPX, sondern für Razor, aber ein ziemlich kompromittierbares Problem.

Ich habe es behoben, indem ich der Anfrage die AntiForgery hinzugefügt habe. Der HTML-Helfer erstellt mit dem Aufruf keine HTML-ID

@Html.AntiForgeryToken()

Um das Token zur Nachanforderung hinzuzufügen, habe ich gerade die AntiForgery-ID mit jquery zum ausgeblendeten Feld hinzugefügt:

$("input[name*='__RequestVerificationToken']").attr('id', '__AjaxAntiForgeryForm');

Dies führte dazu, dass der Controller die Anforderung mit dem Attribut [ValidateAntiForgeryToken] akzeptierte

Dominik Sand
quelle
-3

AntiforgeryToken ist immer noch ein Schmerz, keines der obigen Beispiele hat Wort für Wort für mich funktioniert. Zu viele dafür sind da. Also habe ich sie alle kombiniert. Benötigen Sie ein @ Html.AntiforgeryToken in einer Form, die um iirc herum hängt

So gelöst:

function Forgizzle(eggs) {
    eggs.__RequestVerificationToken =  $($("input[name=__RequestVerificationToken]")[0]).val();
    return eggs;
}

$.ajax({
            url: url,
            type: 'post',
            data: Forgizzle({ id: id, sweets: milkway }),
});

Fügen Sie im Zweifelsfall weitere $ -Zeichen hinzu

Hazza
quelle