it-swarm.com.de

jQuery Ajax-Aufrufe und das Html.AntiForgeryToken ()

Ich habe in meiner App die Eindämmung von CSRF-Angriffen implementiert nach den Informationen, die ich in einigen Blogbeiträgen im Internet gelesen habe. Insbesondere diese Beiträge waren 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 den [ValidateAntiForgeryToken] für jede Aktion hinzu, die das Http-Verb POST akzeptiert

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

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

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

In einigen Teilen meiner App mache ich Ajax-POSTs mit jQuery auf dem Server, ohne dass ich irgendwelche Form habe. Dies ist beispielsweise der Fall, wenn der Benutzer auf ein Bild klicken darf, 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, in dem "Aktivität als abgeschlossen markieren" angezeigt wird. Wenn der Benutzer auf diese Aktivität klickt, mache ich Ajax POST wie im folgenden Beispiel:

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

Wie kann ich die <%= Html.AntiForgeryToken() %> in diesen Fällen verwenden? Soll ich den Helfer-Aufruf in den Datenparameter des Ajax-Aufrufs aufnehmen?

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

EDIT:

Wie in jayrdub answer habe ich auf folgende 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) {
            // ....
        }
    });
});
189
Lorenzo

Ich verwende 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) {
        // ....
    }
});
233
JeremyWeir

Ich mag die von 360Airwalk angebotene Lösung, kann jedoch etwas verbessert werden. 

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

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

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

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

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

Es verwendet HTTP-Header, anstatt zu versuchen, die Formularauflistung 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 () {
        ...
    }
});
20
viggity

Verwenden Sie nicht Html.AntiForgeryToken. Verwenden Sie stattdessen AntiForgery.GetTokens und AntiForgery.Validate von Web API , wie in Verhindern von Cross-Site Request Forgery-Angriffen (CSRF) .

19
Edward Brey

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

Um Ajax-Anforderungen ordnungsgemäß abzuwickeln, muss das Anti-Fälschungs-Token bei Ajax-Aufrufen an den Server übergeben werden. Die Integration in Ihre Postdaten und Modelle ist unübersichtlich und unnötig. Das Hinzufügen des Tokens als benutzerdefinierte Kopfzeile ist sauber und wiederverwendbar - und Sie können es so konfigurieren, dass Sie nicht jedes Mal daran denken müssen.

Es gibt eine Ausnahme - Unauffälliges Ajax benötigt keine besondere Behandlung für Ajax-Anrufe. Das Token wird wie üblich im regulären ausgeblendeten Eingabefeld übergeben. Genau das gleiche wie ein normaler POST.

_Layout.cshtml

In _layout.cshtml habe ich diesen JavaScript-Block. Es schreibt das Token nicht in das DOM, sondern extrahiert es mit jQuery aus dem versteckten Eingabealiteral, das der MVC-Helper generiert. Die Magic-Zeichenfolge, die den Headernamen darstellt, ist als Konstante in der Attributklasse 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 beforeSend-Funktion. Das gerenderte Eingabeelement verwendet Anführungszeichen, durch die das JavaScript-Literal beschädigt wird.

Client JavaScript

Wenn dies ausgeführt wird, wird die Funktion beforeSend oben 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
  }
});

Server-Bibliothek

Ein benutzerdefiniertes Attribut ist erforderlich, um das nicht standardmäßige Token zu verarbeiten. Dies baut auf der @ viggity-Lösung auf, behandelt aber unauffälliges Ajax richtig. Dieser Code kann in Ihrer allgemeinen Bibliothek abgelegt 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 überprüft.

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

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

Zuerst entschied ich mich, meine Jquery-Ajax-Aufrufe zu aktivieren, damit ich mich nicht zu oft wiederholte. Dieses Javascript-Snippet stellt sicher, dass bei allen Ajax-Aufrufen (Postaufrufen) der Anforderung mein Bestätigungstoken für Anforderungen hinzugefügt wird. Hinweis: Der Name __RequestVerificationToken wird vom .NET-Framework verwendet, sodass ich die standardmäßigen Anti-CSRF-Funktionen wie unten gezeigt 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 obige Javascript verfügbar sein muss, einfach den üblichen HTML-Helper. Sie können diesen Code grundsätzlich hinzufügen, wo immer Sie möchten. Ich habe es in eine if (Request.IsAuthenticated) -Anweisung gestellt:

@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 standardmäßigen 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 Ihren POST - Anforderungen jetzt ein __RequestVerificationToken-Parameter angehängt ist.

15
360Airwalk

Ich denke, Sie müssen nur sicherstellen, dass die Eingabe "__RequestVerificationToken" in der Anforderung POST enthalten ist. Die andere Hälfte der Informationen (d. H. Das Token im Cookie des Benutzers) wird bereits automatisch mit der Anforderung AJAX POST 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) {
            // ....
        }
    });
});
11
jball

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 verwendet Razor, aber wenn Sie die WebForms-Syntax verwenden, können Sie genauso gut <%= %>-Tags verwenden

Nach meinem Kommentar zu @ JBalls Antwort, der mir auf dem Weg geholfen hat, ist dies die letzte Antwort, die für mich funktioniert. Ich verwende MVC und Razor und sende ein Formular mit jQuery AJAX, damit ich eine teilweise Ansicht mit einigen neuen Ergebnissen aktualisieren kann und kein komplettes Postback (und Seitenflimmern) durchführen wollte.

Fügen Sie @Html.AntiForgeryToken() wie üblich in das Formular ein.

Mein AJAX -Einreichungsschaltflächencode (d. H. 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" belassen, 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 von POST-Daten eines ganzen Formulars als Modell bin, aber wenn Sie dies tun müssen, funktioniert dies auf eine Weise. MVC macht die Datenbindung einfach zu einfach. Statt also 16 Einzelwerte (oder eine schwach typisierte FormCollection) anzugeben, ist dies in Ordnung, schätze ich. Wenn Sie es besser wissen, lassen Sie es mich wissen, da ich robusten MVC-C # -Code erstellen möchte.

4
Ralph Bacon

1.Define-Funktion, um Token vom Server zu erhalten

@function
{

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

2.Geben Sie das Token und setzen Sie den Header, bevor Sie ihn 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, mit der Sie Post/get behandeln

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

fand diese sehr clevere Idee aus https://Gist.github.com/scottrippey/3428114 Für jeden Aufruf von $ .ajax ändert er die Anforderung und fügt das Token hinzu. 

// 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();
        }
    }
});
3
masterlopau

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

  $("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
        });
    });
1
Dominic Sputo

Ich weiß, es ist einige Zeit vergangen, seit diese Frage veröffentlicht wurde, aber ich fand eine wirklich nützliche Ressource, die die Verwendung von AntiForgeryToken beschreibt und deren Verwendung weniger mühsam macht. Es stellt auch ein Jquery-Plugin bereit, mit dem leicht ein Anti-Fälschungs-Token in AJAX -Aufrufen eingefügt werden kann:

Antifälschungsanforderungsrezepte für ASP.NET MVC und AJAX

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

1
slawek

Ich verwende einen Ajax-Beitrag, um eine Löschmethode auszuführen (zufällig von einer visjs-Zeitleiste, aber das ist 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(), damit das Token auf der Seite angezeigt wird

Dann habe ich in meinem Ajax-Beitrag Folgendes verwendet:

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

Dies fügt den von der Seite abgekratzten Tokenwert zu den geposteten Feldern hinzu

Vorher habe ich versucht, den Wert in die Kopfzeilen zu setzen, aber ich habe den gleichen Fehler erhalten

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

0
Nick.McDermaid

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);
            }
        }
    });
});
0
Barry MSIH

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);
        }
0
Amir Reza
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 "";
    }
}
0
ismail eski

Okay, viele Beiträge hier, keiner von ihnen hat mir geholfen, Tage und Tage von Google, und trotzdem kam ich nicht weiter zum Punkt, als ich die gesamte App von Grund auf schrieb, und dann bemerkte ich dieses kleine Nugget in meiner Web.confg

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

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

Für mich war die Lösung eine von zwei Optionen, da ich mich nicht daran erinnere, warum ich sie hinzugefügt habe. Ich kann nicht sicher sein, dass andere Dinge davon abhängen, und zweitens muss der Domainname alle Kleinbuchstaben sein und eine TLD ist nicht wie bei mir in * .localLookup.net 

Vielleicht hilft es vielleicht nicht. Ich hoffe es hilft jemandem

0
Agitated