Appels jQuery Ajax et Html.AntiForgeryToken ()

207

J'ai mis en œuvre dans mon application l'atténuation des attaques CSRF suite aux informations que j'ai lues sur certains articles de blog sur Internet. En particulier, ces postes ont été le moteur de ma mise en œuvre

Fondamentalement, ces articles et recommandations indiquent que pour empêcher l'attaque CSRF, quiconque devrait implémenter le code suivant:

1) Ajoutez le [ValidateAntiForgeryToken]sur chaque action qui accepte le verbe Http POST

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

2) Ajoutez l' <%= Html.AntiForgeryToken() %>assistant à l'intérieur des formulaires qui soumet des données au serveur

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

Quoi qu'il en soit, dans certaines parties de mon application, je fais des POST Ajax avec jQuery sur le serveur sans aucune forme. Cela se produit par exemple lorsque je laisse l'utilisateur cliquer sur une image pour effectuer une action spécifique.

Supposons que j'ai une table avec une liste d'activités. J'ai une image sur une colonne du tableau qui dit "Marquer l'activité comme terminée" et lorsque l'utilisateur clique sur cette activité, je fais le POST Ajax comme dans l'exemple suivant:

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

Comment puis-je utiliser le <%= Html.AntiForgeryToken() %>dans ces cas? Dois-je inclure l'appel d'aide dans le paramètre de données de l'appel Ajax?

Désolé pour le long post et merci beaucoup pour votre aide

MODIFIER :

Selon la réponse de jayrdub, j'ai utilisé de la manière suivante

$("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
la source
Le lien David Hayden a maintenant 404, il semble qu'il ait migré son blog vers un nouveau CMS, mais n'a pas migré tout l'ancien contenu.

Réponses:

252

J'utilise une simple fonction js comme celle-ci

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

Étant donné que chaque formulaire sur une page aura la même valeur pour le jeton, mettez simplement quelque chose comme ça dans votre page maître la plus élevée

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

Ensuite, dans votre appel ajax do (modifié pour correspondre à votre deuxième exemple)

$.ajax({
    type: "post",
    dataType: "html",
    url: $(this).attr("rel"),
    data: AddAntiForgeryToken({ id: parseInt($(this).attr("title")) }),
    success: function (response) {
        // ....
    }
});
JeremyWeir
la source
6
Bien, j'aime l'encapsulation de la récupération des jetons.
jball
2
@Lorenzo, mettez vos données personnalisées dans l'appel AddAntiForgeryToken, comme ceci:data: AddAntiForgeryToken({ id: parseInt($(this).attr("title")) }),
jball
3
Quelle serait la mauvaise idée d'utiliser ajaxSendou de remplacer ajaxpour toujours augmenter datale jeton anti-contrefaçon? Peut-être en ajoutant une vérification pour vous assurer que le urlest destiné à votre serveur.
ta.speot.is
1
Soyez prudent si vous utilisez le cache de sortie.
Alp Barbaros
1
@SouhaiebBesbes, le jeton de validation doit être le même pour un utilisateur sur toutes les pages (il fonctionne en conjonction avec un cookie qui est défini et reste le même). Donc, peu importe s'il y a plusieurs demandes par page, ce sera la même chose si la page de base est rechargée de toute façon.
JeremyWeir
29

J'aime la solution fournie par 360Airwalk, mais elle peut être un peu améliorée.

Le premier problème est que si vous créez $.post()des données vides, jQuery n'ajoute pas d'en- Content-Typetête et, dans ce cas, ASP.NET MVC ne parvient pas à recevoir et à vérifier le jeton. Vous devez donc vous assurer que l'en-tête est toujours là.

Une autre amélioration est la prise en charge de tous les verbes HTTP avec du contenu : POST, PUT, DELETE etc. Bien que vous puissiez utiliser uniquement des POST dans votre application, il est préférable d'avoir une solution générique et de vérifier que toutes les données que vous recevez avec n'importe quel verbe ont un anti-faux jeton.

$(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
la source
1
+1 vous avez raison, je n'ai pas pensé au problème de post-appel vide. merci pour l'entrée. vous aviez raison sur le fait que nous n'utilisons pas encore supprimer / mettre dans notre projet.
360Airwalk
2
+1 pour m'avoir épargné d'avoir à ajouter la fonction à tous les appels jQuery.Ajax
Dragos Durlut
2
+1 Tout comme une note pour la postérité, la documentation jQuery pour les .ajaxSend()états "À partir de jQuery 1.8, la méthode .ajaxSend () ne doit être attachée qu'au document." api.jquery.com/ajaxsend
RJ Cuthbertson
1
@Bronx D'où vient le optionsproduit, qui est répertorié dans la ifdéclaration finale ? Merci.
hvaughan3
Attention à utiliser ceci si vous avez plusieurs formulaires sur une page. Vous devrez définir la valeur dans beforeSend avec un appel de sélecteur plus spécifique au lieu de pour le document.
Dan
22

Je sais qu'il y a beaucoup d'autres réponses, mais cet article est agréable et concis et vous oblige à vérifier tous vos HttpPosts, pas seulement certaines d'entre elles:

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

Il utilise des en-têtes HTTP au lieu d'essayer de modifier la collection de formulaires.

Serveur

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

Client

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 () {
        ...
    }
});
vigueur
la source
4
L'attribut de l'article que vous avez lié trop combiné avec la réponse du Bronx est la solution SECHE ultime à ce problème.
TugboatCaptain
2
Super trouvaille. J'ai modifié votre réponse pour inclure les extraits de code afin que la réponse soit autonome, mais j'espère que les gens liront également le reste de l'article. Cela semble être une solution très propre.
Tim Medora
merci Tim, c'est une excellente idée, c'est frustrant quand un lien s'éteint et que la réponse devient sans valeur. J'ai commencé à faire cela sur toutes mes nouvelles réponses.
viggity
Est-ce MVC, WebAPI ou .NetCore? Je ne peux pas obtenir les espaces de noms corrects pour WebAPI 5
Myster
20

Je me sens comme un nécromancien avancé ici, mais c'est toujours un problème 4 ans plus tard dans MVC5.

Pour gérer correctement les requêtes ajax, le jeton anti-falsification doit être transmis au serveur lors des appels ajax. L'intégrer dans vos données et modèles de publication est compliqué et inutile. L'ajout du jeton en tant qu'en-tête personnalisé est propre et réutilisable - et vous pouvez le configurer de sorte que vous n'ayez pas à vous souvenir de le faire à chaque fois.

Il existe une exception - ajax discret n'a pas besoin d'un traitement spécial pour les appels ajax. Le jeton est transmis comme d'habitude dans le champ de saisie caché normal. Exactement la même chose qu'un POST ordinaire.

_Layout.cshtml

Dans _layout.cshtml, j'ai ce bloc JavaScript. Il n'écrit pas le jeton dans le DOM, il utilise plutôt jQuery pour l'extraire du littéral d'entrée caché généré par MVC Helper. La chaîne Magic qui est le nom de l'en-tête est définie comme une constante dans la classe d'attributs.

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

Notez l'utilisation de guillemets simples dans la fonction beforeSend - l'élément d'entrée qui est rendu utilise des guillemets doubles qui briseraient le littéral JavaScript.

JavaScript client

Lorsque cela s'exécute, la fonction beforeSend ci-dessus est appelée et l'AntiForgeryToken est automatiquement ajouté aux en-têtes de demande.

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

Bibliothèque de serveur

Un attribut personnalisé est requis pour traiter le jeton non standard. Cela s'appuie sur la solution de @ viggity, mais gère correctement ajax discret. Ce code peut être caché dans votre bibliothèque commune

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

Serveur / Contrôleur

Maintenant, vous appliquez simplement l'attribut à votre action. Encore mieux, vous pouvez appliquer l'attribut à votre contrôleur et toutes les demandes seront validées.

[HttpPost]
[ValidateAntiForgeryTokenOnAllPosts]
public virtual ActionResult CSRFProtectedMethod()
{
  return Json(true, JsonRequestBehavior.DenyGet);
}
Will D
la source
Solution parfaite, beaucoup plus centralisée. Merci
David Freire
Pouvez-vous expliquer plus en détail pourquoi vous ne souhaitez ajouter que l'en-tête des URL relatives? Cela m'a dépassé la tête. Excellente solution!
MattM
relative garantit que l'en-tête n'est défini que sur les requêtes revenant à votre propre serveur, car la configuration ajax couvre toutes les requêtes effectuées avec jquery, nous ne voulons pas que le jeton soit envoyé sur les requêtes jsonp ou CORS. Cela peut également être vrai pour les URL absolues, mais il est garanti que le même domaine est relatif.
Will D
1
@WillD J'ai aimé votre solution, mais j'ai été obligée de la modifier un peu. Comme vous choisissez $.ajaxSetupde définir un beforesendgestionnaire d'événements général, il peut arriver que vous le remplaciez. J'ai trouvé une autre solution où vous pouvez ajouter un deuxième gestionnaire qui sera également appelé. Fonctionne bien et ne rompt pas votre implémentation.
Viper
Quelqu'un a-t-il une version ASP.net 5 du client pour valider l'attribut AntiForgery? Cette version ne se compile pas dans la dernière version!
Rob McCabe
19

N'utilisez pas Html.AntiForgeryToken . À la place, utilisez AntiForgery.GetTokens et AntiForgery.Validate à partir de l'API Web, comme décrit dans la section Prévention des attaques CSRF (Cross-Site Request Forgery) dans l'application ASP.NET MVC .

Edward Brey
la source
Pour les méthodes d'action de contrôleur qui modélisent un modèle de serveur pour le modèle JSON AJAX publié, le type de contenu "application / json" est requis pour utiliser le classeur de modèle approprié. Malheureusement, cela empêche d'utiliser les données du formulaire, requises par l'attribut [ValidateAntiForgeryToken], donc votre méthode est le seul moyen que j'ai pu trouver pour le faire fonctionner. Ma seule question est la suivante: cela fonctionne-t-il toujours dans une batterie de serveurs Web ou plusieurs instances de rôle Web Azure? Vous @Edward, ou quelqu'un d'autre sait-il si c'est un problème?
Richard B
@Edward Brey Pouvez-vous expliquer pourquoi nous ne devrions pas l'utiliser?
Odys
4
@Odys: il n'y a rien de mal à Html.AntiForgeryToken, mais il a des inconvénients: nécessite un formulaire, nécessite jQuery et suppose des détails d'implémentation Html.AntiForgeryToken non documentés. Pourtant, c'est bien dans de nombreux contextes. Mon énoncé «N'utilisez pas Html.AntiForgeryToken» semble probablement trop fort. Mon sens est qu'il n'est pas destiné à être utilisé avec l'API Web, contrairement à AntiForgery.GetTokens, plus flexible.
Edward Brey
THX! J'ai dû le changer un peu pour le faire fonctionner pour un contrôleur MVC5, mais c'était la solution
jao
3
Cela ne nécessite certainement pas de formulaire. Vous avez juste besoin d'analyser le DOM par son nom. En utilisant jquery, je peux l'ajouter à l'intérieur de mon objet de données via data {__RequestVerificationToken: $ ("input [name = __ RequestVerificationToken]"). Val ()}
Anthony Mason
16

J'étais juste en train d'implémenter ce problème réel dans mon projet actuel. je l'ai fait pour tous les ajax-POST qui avaient besoin d'un utilisateur authentifié.

Tout d'abord, j'ai décidé de raccrocher mes appels jquery ajax afin de ne pas me répéter trop souvent. cet extrait javascript garantit que tous les appels ajax (post) ajouteront mon jeton de validation de demande à la demande. Remarque: le nom __RequestVerificationToken est utilisé par le framework .Net afin que je puisse utiliser les fonctionnalités anti-CSRF standard comme indiqué ci-dessous.

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

Dans vos vues où vous avez besoin que le jeton soit disponible pour le javascript ci-dessus, utilisez simplement le HTML-Helper commun. Vous pouvez essentiellement ajouter ce code où vous le souhaitez. Je l'ai placé dans une instruction if (Request.IsAuthenticated):

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

Dans votre contrôleur, utilisez simplement le mécanisme standard ASP.Net MVC Anti-CSRF. Je l'ai fait comme ça (même si j'ai utilisé du sel).

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

Avec Firebug ou un outil similaire, vous pouvez facilement voir comment vos requêtes POST ont désormais un paramètre __RequestVerificationToken ajouté.

360Airwalk
la source
15

Je pense que tout ce que vous avez à faire est de vous assurer que l'entrée "__RequestVerificationToken" est incluse dans la requête POST. L'autre moitié des informations (c'est-à-dire le jeton dans le cookie de l'utilisateur) est déjà envoyée automatiquement avec une demande AJAX POST.

Par exemple,

$("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
la source
1
Après plusieurs heures à expérimenter avec la publication jQuery AJAX à partir d'une page MVC (Razor), c'était la réponse la plus simple de tout ce qui fonctionnait pour moi. Incluez simplement vos propres champs de données (ou le viewModel je suppose) après le jeton en tant que nouvelle donnée (mais dans l'objet de données d'origine).
Ralph Bacon
Comment pourrais-je implémenter cela si la fonction AJAX était dans une page .html et non une page Razor?
Bob le constructeur
Si votre page html n'a pas de serveur fourni AntiForgeryToken, tout est discutable de toute façon. Si c'est le cas (je ne sais pas comment vous en obtenez un dans ce cas, mais en supposant que vous l'êtes), alors ce qui précède fonctionnerait très bien. Si vous essayez de créer une page Web simple qui publiera une demande sur un serveur attendant ledit jeton et que le serveur n'a pas généré ladite page, alors vous n'avez pas de chance. C'est essentiellement le but de l'AntiForgeryToken ...
jball
6

Vous pouvez également le faire:

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

Cela utilise Razor, mais si vous utilisez la WebFormssyntaxe, vous pouvez tout aussi bien utiliser des <%= %>balises

Leonardo Garcia Crespo
la source
4

Suite à mon commentaire contre la réponse de @ JBall qui m'a aidé en cours de route, c'est la réponse finale qui fonctionne pour moi. J'utilise MVC et Razor et je soumets un formulaire en utilisant jQuery AJAX afin que je puisse mettre à jour une vue partielle avec de nouveaux résultats et je ne voulais pas faire une publication complète (et un scintillement de page).

Ajoutez l' @Html.AntiForgeryToken()intérieur du formulaire comme d'habitude.

Mon code de bouton de soumission AJAX (c'est-à-dire un événement onclick) est:

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

J'ai laissé l'action "succès" car elle montre comment la vue partielle est mise à jour qui contient un MvcJqGrid et comment il est actualisé (grille jqGrid très puissante et c'est un brillant wrapper MVC pour cela).

Ma méthode de contrôleur ressemble à ceci:

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

Je dois admettre que je ne suis pas un fan de la publication de données d'un formulaire entier en tant que modèle, mais si vous devez le faire, c'est une façon de fonctionner. MVC rend simplement la liaison de données trop facile, plutôt que de soumettre 16 valeurs individuelles (ou une FormCollection faiblement typée), cela est OK, je suppose. Si vous savez mieux, faites-le moi savoir car je veux produire un code MVC C # robuste.

Ralph Bacon
la source
4

a trouvé cette idée très intelligente sur https://gist.github.com/scottrippey/3428114 pour chaque appel $ .ajax, il modifie la demande et ajoute le jeton.

// 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
la source
J'ai essayé plusieurs des autres alternatives ci-dessus, c'est ce qui l'a résolu pour moi.
HostMyBus
J'ai toutefois dû ajouter if (options.contentType != false && options.contentType.indexOf('application/json') === 0) {pour intercepter les appels Ajax qui n'avaient pas spécifié de type de contenu
HostMyBus
3

1. définir la fonction pour obtenir le jeton du serveur

@function
{

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

Obtenez le jeton et définissez l'en-tête avant de l'envoyer au serveur

var token = '@TokenHeaderValue()';    

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

3. Onserver Validation on HttpRequestBase on method you handle 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 seul
la source
1

Je sais que cela fait un certain temps que cette question n'a pas été publiée, mais j'ai trouvé une ressource vraiment utile, qui discute de l'utilisation d'AntiForgeryToken et la rend moins gênante à utiliser. Il fournit également un plugin jquery pour inclure facilement un jeton anti-corruption dans les appels AJAX:

Recettes de demande anti-contrefaçon pour ASP.NET MVC et AJAX

Je ne contribue pas beaucoup, mais peut-être que quelqu'un le trouvera utile.

slawek
la source
Ce message est comme un mile de long! Je suis sûr que c'est génial mais tl; dr
BritishDeveloper
1
Dommage, car il couvre bien le sujet. Il vous indique non seulement comment utiliser la fonctionnalité, mais explique le problème qu'elle résout et vous donne un contexte pour comprendre comment l'utiliser correctement. En ce qui concerne la sécurité, je pense qu'une compréhension approfondie est importante.
slawek
2
Si c'est important, il doit être écrit d'une manière qui encourage les gens à le lire;)
BritishDeveloper
1

utilisez d'abord @ Html.AntiForgeryToken () en 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
la source
1

Voici la façon la plus simple que j'ai vue. Remarque: Assurez-vous que vous avez "@ Html.AntiForgeryToken ()" dans votre vue

  $("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
la source
0

Légère amélioration de la solution 360Airwalk. Cela incorpore le jeton anti-contrefaçon dans la fonction javascript, donc @ Html.AntiForgeryToken () n'a plus besoin d'être inclus dans chaque vue.

$(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
la source
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
la source
0

J'utilise un message ajax pour exécuter une méthode de suppression (se trouve être à partir d'une chronologie visjs mais ce n'est pas pertinent). Voilà ce que je sis:

Ceci est mon 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>

Tout ce que j'ai ajouté ici était @Html.AntiForgeryToken() de faire apparaître le jeton dans la page

Ensuite, dans mon post ajax, j'ai utilisé:

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

Ce qui ajoute la valeur du jeton, grattée sur la page, aux champs publiés

Avant cela, j'ai essayé de mettre la valeur dans les en-têtes, mais j'ai eu la même erreur

N'hésitez pas à publier des améliorations. Cela semble certainement être une approche simple que je peux comprendre

Nick.McDermaid
la source
0

D'accord, beaucoup de messages ici, aucun d'eux ne m'a aidé, des jours et des jours de google, et toujours pas plus je suis arrivé au point d'écrire toute l'application à partir de zéro, puis j'ai remarqué ce petit nugget dans mon Web.confg

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

Maintenant, je ne sais pas pourquoi je l'ai ajouté, mais je l'ai remarqué depuis, il est ignoré en mode débogage et non en mode production (IE installé sur IIS quelque part)

Pour moi, la solution était l'une des 2 options, car je ne me souviens pas pourquoi je l'ai ajouté, je ne peux pas être sûr que d'autres choses n'en dépendent pas, et en second lieu le nom de domaine doit être tout en minuscules et un TLD pas comme ive done dans * .localLookup.net

Peut-être que cela aide peut-être que non. J'espère que ça aide quelqu'un

Agité
la source
0

La solution que j'ai trouvée n'est pas pour ASPX mais pour Razor, mais un problème tout à fait compérable.

Je l'ai résolu en ajoutant l'AntiForgery à la demande. L'assistant HTML ne crée pas d'ID HTML avec l'appel

@Html.AntiForgeryToken()

Afin d'ajouter le jeton à la postrequest, je viens d'ajouter l'identifiant AntiForgery au champ caché avec jquery:

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

Cela a amené le contrôleur à accepter la demande avec l'attribut [ValidateAntiForgeryToken]

Dominik Sand
la source
-3

AntiforgeryToken est toujours une douleur, aucun des exemples ci-dessus n'a fonctionné mot pour mot pour moi. Il y en a trop pour. Je les ai donc tous combinés. Besoin d'un @ Html.AntiforgeryToken sous une forme suspendue à iirc

Résolu comme tel:

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

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

En cas de doute, ajoutez plus de signes $

Hazza
la source