Wywołania jQuery Ajax i Html.AntiForgeryToken ()

207

W mojej aplikacji zaimplementowałem ograniczenie do ataków CSRF zgodnie z informacjami, które przeczytałem na pewnym blogu w Internecie. W szczególności te posty były motorem mojej implementacji

Zasadniczo te artykuły i rekomendacje mówią, że aby zapobiec atakowi CSRF, każdy powinien wdrożyć następujący kod:

1) Dodaj [ValidateAntiForgeryToken]na każdej akcji, która akceptuje czasownik HTTP POST

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

2) Dodaj <%= Html.AntiForgeryToken() %>pomocnika do formularzy, które przesyłają dane do serwera

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

W każdym razie w niektórych częściach mojej aplikacji wykonuję testy Ajax POST z jQuery na serwerze, bez żadnej formy. Dzieje się tak na przykład, gdy pozwalam użytkownikowi kliknąć obraz, aby wykonać określoną akcję.

Załóżmy, że mam tabelę z listą działań. Mam obraz w kolumnie tabeli z napisem „Oznacz aktywność jako zakończoną”, a kiedy użytkownik kliknie tę aktywność, wykonuję test POST Ajax, jak w poniższym przykładzie:

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

Jak mogę wykorzystać <%= Html.AntiForgeryToken() %>w tych przypadkach? Czy powinienem dołączyć wywołanie pomocnika do parametru danych wywołania Ajax?

Przepraszamy za długi post i bardzo dziękuję za pomoc

EDYCJA :

Zgodnie z odpowiedzią jayrdub użyłem w następujący sposób

$("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
źródło
Link David Hayden teraz błędy 404, wydaje się, że to jego blog przeniesione do nowego systemu CMS, ale nie przenieść wszystkie stare treści nad.

Odpowiedzi:

252

Używam prostej funkcji js takiej jak ta

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

Ponieważ każdy formularz na stronie będzie miał tę samą wartość dla tokena, po prostu umieść coś takiego na najwyższej stronie wzorcowej

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

Następnie w wywołaniu ajax do (edytowane w celu dopasowania do drugiego przykładu)

$.ajax({
    type: "post",
    dataType: "html",
    url: $(this).attr("rel"),
    data: AddAntiForgeryToken({ id: parseInt($(this).attr("title")) }),
    success: function (response) {
        // ....
    }
});
JeremyWeir
źródło
6
Fajnie, podoba mi się enkapsulacja pobierania tokena.
jball
2
@ Lorenzo, umieść własne dane w wywołaniu AddAntiForgeryToken, na przykład:data: AddAntiForgeryToken({ id: parseInt($(this).attr("title")) }),
jball
3
Jak kiepskim pomysłem byłoby użycie ajaxSendlub zastąpienie, ajaxaby zawsze ulepszyć datatoken zapobiegający fałszowaniu? Może dodając trochę czeku, aby upewnić się, że urljest on przeznaczony dla twojego serwera.
ta.speot.is
1
Zachowaj ostrożność, jeśli używasz wyjściowej pamięci podręcznej.
Barbaros Alp,
1
@SouhaiebBesbes token sprawdzania poprawności powinien być taki sam dla użytkownika na wszystkich stronach (działa w połączeniu z ustawionym plikiem cookie i pozostaje taki sam). Nie ma więc znaczenia, czy na stronie jest wiele żądań, tak samo będzie, jeśli strona podstawowa i tak zostanie ponownie załadowana.
JeremyWeir
29

Podoba mi się rozwiązanie dostarczone przez 360Airwalk, ale można je nieco ulepszyć.

Pierwszy problem polega na tym, że jeśli robisz $.post()z pustymi danymi, jQuery nie dodaje Content-Typenagłówka, aw tym przypadku ASP.NET MVC nie odbiera i nie sprawdza tokena. Musisz więc upewnić się, że nagłówek jest zawsze dostępny.

Kolejnym ulepszeniem jest obsługa wszystkich czasowników HTTP z zawartością : POST, PUT, DELETE itp. Chociaż możesz używać tylko POST w swojej aplikacji, lepiej mieć ogólne rozwiązanie i sprawdzić, czy wszystkie dane otrzymane za pomocą dowolnego czasownika są zabezpieczone przed fałszowaniem znak.

$(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
źródło
1
+1 masz rację, nie myślałem o problemie pustej rozmowy telefonicznej. dzięki za wkład. miałeś rację co do tego, że nie korzystamy jeszcze z funkcji usuwania / umieszczania w naszym projekcie.
360Airwalk
2
+1 za uratowanie mnie przed dodaniem funkcji do wszystkich wywołań jQuery.Ajax
Dragos Durlut
2
+1 Tak jak uwaga dla potomności, dokumentacja jQuery dla .ajaxSend()stanów „Począwszy od jQuery 1.8, metoda .ajaxSend () powinna być dołączana tylko do dokumentu”. api.jquery.com/ajaxsend
RJ Cuthbertson
1
@Bronx Skąd optionspochodzi, co jest wymienione w końcowym ifzestawieniu? Dzięki.
hvaughan3 20.04.16
Wystrzegaj się tego, jeśli masz wiele formularzy na stronie. Będziesz musiał ustawić wartość w beforeSend z bardziej szczegółowym wywołaniem selektora zamiast dla dokumentu.
Dan
22

Wiem, że istnieje wiele innych odpowiedzi, ale ten artykuł jest miły i zwięzły i zmusza cię do sprawdzenia wszystkich swoich HttpPosts, a nie tylko niektórych z nich:

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

Używa nagłówków HTTP zamiast próbować modyfikować kolekcję formularzy.

serwer

//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 () {
        ...
    }
});
energiczność
źródło
4
Atrybut z artykułu, który również połączyłeś w połączeniu z odpowiedzią Bronx, jest najlepszym SUCHYM rozwiązaniem tego problemu.
TugboatCaptain
2
Świetne znalezisko. Zredagowałem twoją odpowiedź, aby dołączyć fragmenty kodu, więc odpowiedź jest samodzielna, ale mam nadzieję, że ludzie również przeczytają resztę artykułu. To wydaje się być bardzo czystym rozwiązaniem.
Tim Medora
dzięki Tim, jest to doskonały pomysł, frustrujący, gdy link znika, a odpowiedź staje się bezwartościowa. Zacząłem to robić na wszystkich moich nowych odpowiedziach.
wiggity
Czy to MVC, WebAPI lub .NetCore? Nie mogę uzyskać poprawnych przestrzeni nazw dla WebAPI 5
Myster
20

Czuję się tutaj jak zaawansowany nekromanta, ale nadal jest to problem 4 lata później w MVC5.

Aby poprawnie obsłużyć żądania ajax, token zapobiegający fałszowaniu musi zostać przekazany do serwera podczas wywołań ajax. Zintegrowanie go z danymi i modelami postów jest nieuporządkowane i niepotrzebne. Dodanie tokena jako niestandardowego nagłówka jest czyste i wielokrotnego użytku - i możesz go skonfigurować, abyś nie musiał pamiętać, aby to robić za każdym razem.

Istnieje wyjątek - dyskretne ajax nie wymaga specjalnego traktowania dla wywołań ajax. Token jest przekazywany jak zwykle w zwykłym ukrytym polu wejściowym. Dokładnie tak samo jak zwykły test POST.

_Layout.cshtml

W _layout.cshtml mam ten blok JavaScript. Nie zapisuje tokena w DOM, lecz używa jQuery do wyodrębnienia go z ukrytego literału wejściowego, który generuje MVC Helper. Ciąg Magic, który jest nazwą nagłówka, jest zdefiniowany jako stała w klasie atrybutu.

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

Zwróć uwagę na użycie pojedynczych cudzysłowów w funkcji beforeSend - renderowany element wejściowy używa podwójnych cudzysłowów, które łamałyby literał JavaScript.

JavaScript klienta

Po wykonaniu tej czynności wywoływana jest powyższa funkcja beforeSend, a AntiForgeryToken jest automatycznie dodawany do nagłówków żądania.

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

Biblioteka serwerów

Atrybut niestandardowy jest wymagany do przetworzenia niestandardowego tokena. To opiera się na rozwiązaniu @ viggity, ale poprawnie obsługuje dyskretne ajax. Ten kod można schować we wspólnej bibliotece

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

Serwer / kontroler

Teraz po prostu zastosujesz atrybut do swojej akcji. Jeszcze lepiej możesz zastosować ten atrybut do kontrolera, a wszystkie żądania zostaną zatwierdzone.

[HttpPost]
[ValidateAntiForgeryTokenOnAllPosts]
public virtual ActionResult CSRFProtectedMethod()
{
  return Json(true, JsonRequestBehavior.DenyGet);
}
Czy D.
źródło
Idealne rozwiązanie, znacznie bardziej scentralizowane. Dzięki
David Freire,
Czy możesz wyjaśnić bardziej szczegółowo, dlaczego chcesz dodać tylko nagłówek względnych adresów URL? To przeszło mi przez głowę. Świetne rozwiązanie!
MattM
Względny zapewnia, że ​​nagłówek jest ustawiany tylko w przypadku żądań wracających do twojego serwera, ponieważ konfiguracja ajax obejmuje wszystkie żądania wykonane za pomocą jquery, nie chcemy, aby token był wysyłany w jetsp lub w żądaniach CORS. To może być prawda również w przypadku bezwzględnych adresów URL, ale krewni mają zapewnioną tę samą domenę.
Czy D
1
@WillD Podobało mi się twoje rozwiązanie, ale musiałem go nieco zmodyfikować. Ponieważ zdecydujesz $.ajaxSetupsię zdefiniować ogólny beforesendmoduł obsługi zdarzeń, może się zdarzyć, że go zastąpisz. Znalazłem inne rozwiązanie, w którym możesz dodać drugi moduł obsługi, który również zostanie wywołany. Działa ładnie i nie psuje twojej implementacji.
Viper,
Czy ktoś ma wersję klienta ASP.net 5 sprawdzającą poprawność atrybutu AntiForgery? Ta wersja nie kompiluje się w najnowszej wersji!
Rob McCabe,
19

Nie używaj Html.AntiForgeryToken . Zamiast tego należy użyć funkcji AntiForgery.GetTokens i AntiForgery.Validate z interfejsu API sieci Web, jak opisano w Zapobieganie atakom typu CSRF (Cross-Site Request Forgery) w aplikacji ASP.NET MVC .

Edward Brey
źródło
W przypadku metod działania kontrolera, które modelują powiązanie typu modelu serwera z opublikowanym AJAX JSON, posiadanie typu zawartości jako „application / json” jest wymagane do użycia właściwego spoiwa modelu. Niestety wyklucza to używanie danych formularza, wymaganych przez atrybut [ValidateAntiForgeryToken], więc twoja metoda jest jedynym sposobem, w jaki mogłem znaleźć, aby działała. Moje jedyne pytanie brzmi: czy nadal działa w farmie internetowej lub w wielu instancjach ról sieciowych platformy Azure? Czy ty @Edward lub ktoś inny wie, czy to jest problem?
Richard B,
@ Edward Brey Czy możesz wyjaśnić, dlaczego nie powinniśmy go używać?
Odys,
4
@Odys: W Html.AntiForgeryToken nie ma nic złego, ale ma swoje wady: wymaga formularza, wymaga jQuery i zakłada nieudokumentowane szczegóły implementacji Html.AntiForgeryToken. Mimo to jest dobrze w wielu kontekstach. Moje stwierdzenie „Nie używaj Html.AntiForgeryToken” prawdopodobnie jest zbyt mocne. Mam na myśli, że nie jest przeznaczony do używania z Web API, podczas gdy bardziej elastyczny AntiForgery.GetTokens jest.
Edward Brey
dzięki! Musiałem to nieco zmienić, aby działało w przypadku kontrolera MVC5, ale takie było rozwiązanie
jao
3
Z pewnością nie wymaga formularza. Musisz tylko przeanalizować DOM dla niego według nazwy. Za pomocą jquery mogę dodać go do mojego obiektu danych za pomocą danych {__RequestVerificationToken: $ („input [name = __ RequestVerificationToken]”). Val ()}
Anthony Mason
16

Właśnie wdrażałem ten faktyczny problem w moim obecnym projekcie. Zrobiłem to dla wszystkich ajax-POST, które wymagały uwierzytelnionego użytkownika.

Po pierwsze postanowiłem podciągnąć moje wywołania ajaxowe jquery, więc nie powtarzam się zbyt często. ten fragment kodu javascript zapewnia, że ​​wszystkie wywołania ajax (post) dodadzą do żądania mój token sprawdzania poprawności. Uwaga: nazwa __RequestVerificationToken jest używana przez platformę .Net, dzięki czemu mogę korzystać ze standardowych funkcji Anti-CSRF, jak pokazano poniżej.

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

W swoich widokach, w których potrzebujesz tokena, aby był dostępny dla powyższego javascript, po prostu użyj wspólnego HTML-Helper. Możesz w zasadzie dodać ten kod, gdziekolwiek chcesz. Umieściłem go w instrukcji if (Request.IsAuthenticated):

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

W kontrolerze wystarczy użyć standardowego mechanizmu Anti-CSRF ASP.Net MVC. Zrobiłem to w ten sposób (chociaż faktycznie używałem soli).

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

Za pomocą Firebug lub podobnego narzędzia możesz łatwo zobaczyć, w jaki sposób twoje żądania POST mają teraz dołączony parametr __RequestVerificationToken.

360Airwalk
źródło
15

Myślę, że wszystko, co musisz zrobić, to upewnić się, że dane wejściowe „__RequestVerificationToken” są uwzględnione w żądaniu POST. Druga połowa informacji (tj. Token w pliku cookie użytkownika) jest już wysyłana automatycznie z żądaniem AJAX POST.

Na przykład,

$("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
źródło
1
Po wielu godzinach eksperymentowania z publikowaniem jQuery AJAX ze strony MVC (Razor) była to najprostsza odpowiedź ze wszystkich, które działały dla mnie. Po prostu dołącz własne pola danych (lub przypuszczam, że viewModel) za tokenem jako nowy fragment danych (ale w oryginalnym obiekcie danych).
Ralph Bacon,
Jak mam to zaimplementować, jeśli funkcja AJAX była na stronie .html, a nie na stronie Razor?
Bob the Builder
Jeśli na twojej stronie HTML nie ma serwera, AntiForgeryTokento i tak wszystko jest sporne. Jeśli tak (nie jestem pewien, w jaki sposób otrzymujesz taki w takim przypadku, ale zakładając, że tak), powyższe działałoby dobrze. Jeśli próbujesz utworzyć prostą stronę internetową, która opublikuje żądanie na serwerze oczekującym tego tokena, a serwer nie wygenerował tej strony, to nie masz szczęścia. Taki jest właśnie sens AntiForgeryToken ...
jball
6

Możesz to zrobić także:

$("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) {
        // ....
        }
    });
});

To używa Razor, ale jeśli używasz WebFormsskładni, możesz równie dobrze używać <%= %>znaczników

Leonardo Garcia Crespo
źródło
4

Po moim komentarzu do odpowiedzi @ JBall, która pomogła mi po drodze, jest to ostateczna odpowiedź, która działa dla mnie. Korzystam z MVC i Razor i przesyłam formularz za pomocą jQuery AJAX, dzięki czemu mogę zaktualizować widok częściowy z nowymi wynikami i nie chciałem robić pełnego postback (i migotania strony).

Dodaj @Html.AntiForgeryToken() wnętrze formularza jak zwykle.

Mój kod przycisku przesyłania AJAX (tj. Zdarzenie onclick) to:

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

Zostawiłem akcję „sukces”, ponieważ pokazuje, w jaki sposób aktualizowany jest widok częściowy, który zawiera siatkę MvcJqGrid i jak jest odświeżany (bardzo wydajna siatka jqGrid i jest to doskonałe opakowanie MVC).

Moja metoda kontrolera wygląda następująco:

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

Muszę przyznać, że nie jestem fanem POSTingowania danych całego formularza jako modelu, ale jeśli musisz to zrobić, jest to jeden ze sposobów, który działa. MVC po prostu sprawia, że ​​wiązanie danych jest zbyt łatwe, więc zamiast wysyłać 16 pojedynczych wartości (lub słabo wpisanego FormCollection), to chyba OK. Jeśli wiesz lepiej, daj mi znać, ponieważ chcę stworzyć solidny kod MVC C #.

Ralph Bacon
źródło
4

znalazłem ten bardzo sprytny pomysł z https://gist.github.com/scottrippey/3428114 dla każdego wywołania $ .ajax, modyfikuje żądanie i dodaje 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();
        }
    }
});
masterlopau
źródło
Wypróbowałem kilka innych alternatyw powyżej, to właśnie dla mnie to rozwiązało.
HostMyBus
Musiałem jednak dodać, if (options.contentType != false && options.contentType.indexOf('application/json') === 0) {aby złapać wywołania Ajax, które nie określiły typu zawartości
HostMyBus
3

1. Zdefiniuj funkcję, aby pobrać Token z serwera

@function
{

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

2. Pobierz token i ustaw nagłówek przed wysłaniem na serwer

var token = '@TokenHeaderValue()';    

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

3. Sprawdzanie poprawności Onserver na HttpRequestBase dla metody, którą obsługujesz 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);
Tonman Neverwalk sam
źródło
1

Wiem, że minęło trochę czasu, odkąd to pytanie zostało opublikowane, ale znalazłem naprawdę użyteczny zasób, który omawia użycie AntiForgeryToken i sprawia, że ​​korzystanie z niego jest mniej kłopotliwe. Zapewnia również wtyczkę jquery do łatwego włączania tokenu antygranicznego w połączeniach AJAX:

Przepisy dotyczące wniosków o fałszerstwo dotyczące ASP.NET MVC i AJAX

Nie wnoszę wiele, ale może ktoś uzna to za przydatne.

Sławek
źródło
Ten post ma długość mili! Jestem pewien, że jest świetny, ale tl; dr
BritishDeveloper
1
Szkoda, bo ładnie obejmuje temat. Nie tylko informuje, jak korzystać z tej funkcji, ale wyjaśnia, jaki problem rozwiązuje, i daje kontekst, aby zrozumieć, jak prawidłowo z niej korzystać. Jeśli chodzi o bezpieczeństwo, uważam, że dogłębne zrozumienie jest ważne.
Sławek
2
Jeśli jest to ważne, powinno być napisane w sposób zachęcający do czytania;)
BritishDeveloper 30.04. O
1

najpierw użyj @ Html.AntiForgeryToken () w 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
źródło
1

Oto najprostszy sposób, jaki widziałem. Uwaga: Upewnij się, że masz w widoku „@ Html.AntiForgeryToken ()”

  $("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
źródło
0

Nieznaczne ulepszenie rozwiązania 360Airwalk. To osadza token Anti Forgery w funkcji javascript, więc @ Html.AntiForgeryToken () nie musi już być dołączany do każdego widoku.

$(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
źródło
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
źródło
0

Używam wpisu ajax do uruchomienia metody usuwania (zdarza się, że pochodzi z osi czasu Visjs, ale to nie jest relelvant). Oto co ja siostrzyczko:

To jest mój 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>

Wszystko, co tutaj dodałem, to @Html.AntiForgeryToken() aby token pojawił się na stronie

Następnie w moim poście ajax użyłem:

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

Co dodaje wartość tokena, zeskrobaną ze strony, do opublikowanych pól

Wcześniej próbowałem umieścić wartość w nagłówkach, ale otrzymałem ten sam błąd

Publikuj ulepszenia. To z pewnością wydaje się proste podejście, które mogę zrozumieć

Nick.McDermaid
źródło
0

Dobra, dużo postów tutaj, żaden z nich nie pomógł mi, dni i dni google, i nadal nie doszedłem do tego stopnia, że ​​pisałem całą aplikację od zera, a potem zauważyłem ten mały samorodek na moim Web.confg

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

Teraz nie wiem, dlaczego go dodałem, ale zauważyłem, że jest ignorowany w trybie debugowania, a nie w trybie produkcyjnym (IE zainstalowany gdzieś w IIS)

Dla mnie rozwiązaniem była jedna z 2 opcji, ponieważ nie pamiętam, dlaczego go dodałem, nie mogę się upewnić, że inne rzeczy nie zależą od tego, a po drugie, nazwa domeny musi być małymi literami, a TLD nie tak jak zrobiłem w * .localLookup.net

Może to pomaga, może nie. Mam nadzieję, że to komuś pomoże

Poruszony
źródło
0

Znalezione przeze mnie rozwiązanie nie dotyczy ASPX, ale Razor, ale problem, który można całkiem rozwiązać.

Rozwiązałem to, dodając do żądania AntiForgery. Pomocnik HTML nie tworzy identyfikatora HTML z wywołaniem

@Html.AntiForgeryToken()

Aby dodać token do żądania końcowego, właśnie dodałem identyfikator AntiForgery do ukrytego pola za pomocą jquery:

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

Spowodowało to, że kontroler zaakceptował żądanie z atrybutem [ValidateAntiForgeryToken]

Dominik Sand
źródło
-3

AntiforgeryToken nadal jest uciążliwy, żaden z powyższych przykładów nie działał dla mnie słowo w słowo. Jest ich tam zbyt wielu. Więc połączyłem je wszystkie. Potrzebujesz @ Html.AntiforgeryToken w formie wiszącej wokół iirc

Rozwiązany tak:

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

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

W razie wątpliwości dodaj więcej znaków $

Hazza
źródło