asp.net mvc - jQuery Ajax يدعو و Html.AntiForgeryToken()




asp.net-mvc asp.net-mvc-2 (11)

لقد نفذت في تطبيقي التخفيف من هجمات CSRF بعد المعلومات التي قرأتها على بعض مشاركات المدونة حول الإنترنت. على وجه الخصوص كانت هذه الوظيفة هي محرك التنفيذ الخاص بي

أساسا تلك المقالات والتوصيات تقول أنه لمنع هجوم CSRF يجب على أي شخص تنفيذ التعليمات البرمجية التالية:

1) إضافة [ValidateAntiForgeryToken] على كل إجراء يقبل الفعل POST Http

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

2) أضف المساعد <%= Html.AntiForgeryToken() %> داخل النماذج التي <%= Html.AntiForgeryToken() %> البيانات إلى الخادم

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

على أي حال في بعض أجزاء التطبيق الخاص بي أقوم باجراء اياكس POSTs مع jQuery إلى الخادم دون وجود أي شكل على الإطلاق. يحدث هذا على سبيل المثال حيث أسمح للمستخدم بالنقر على صورة للقيام بإجراء محدد.

لنفترض أن لدي جدولًا يحتوي على قائمة بالأنشطة. لدي صورة على عمود بالجدول تقول "ضع علامة على النشاط عند اكتماله" وعندما ينقر المستخدم على هذا النشاط ، أقوم بـ Ajax POST كما في النموذج التالي:

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

كيف يمكنني استخدام <%= Html.AntiForgeryToken() %> في هذه الحالات؟ هل يجب علي تضمين مكالمة المساعد داخل معلمة البيانات الخاصة بمكالمة Ajax؟

آسف لفترة طويلة وشكرا جزيلا للمساعدة

تحرير :

وفقا jayrdub الجواب لقد استخدمت بالطريقة التالية

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

AntiforgeryToken لا يزال هناك ألم ، أي من الأمثلة المذكورة أعلاه عملت كلمة لكلمة بالنسبة لي. الكثير جدا هناك. لذا قمت بدمجهم جميعًا. تحتاج إلى @ Html.AntiforgeryToken في شكل معلق حول iirc

تم حلها على النحو التالي:

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

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

عندما تكون في شك ، إضافة المزيد من علامات $


أشعر أن مستحضر الأرواح متطور هنا ، لكن هذا لا يزال مشكلة بعد 4 سنوات في MVC5.

لمعالجة طلبات ajax بشكل صحيح ، يجب تمرير الرمز المميز لمكافحة التزوير إلى الخادم على مكالمات ajax. يعتبر دمجها في بيانات ونماذج المشاركة أمرًا غير ضروري وغير ضروري. تعد إضافة الرمز المميز كرأس مخصص نظيفًا وقابل لإعادة الاستخدام - ويمكنك تهيئته حتى لا تضطر إلى تذكر القيام به في كل مرة.

هناك استثناء - لا يحتاج ajax غير مزعج إلى معاملة خاصة لمكالمات ajax. يتم تمرير الرمز المميز كالمعتاد في حقل الإدخال المخفي العادي. تمامًا مثل POST العادي.

_Layout.cshtml

في _layout.cshtml لديّ كتلة جافا سكريبت هذه. لا يكتب الرمز المميز في DOM ، بل يستخدم jQuery لاستخلاصه من معلمة الإدخال المخفية التي ينشئها MVC Helper. يتم تعريف السلسلة السحرية التي هي اسم رأس الثابت في فئة السمة.

<script type="text/javascript">
    $(document).ready(function () {
        var isAbsoluteURI = new RegExp('^(?:[a-z]+:)?//', 'i');
        //http://.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>

لاحظ استخدام علامات الاقتباس المفردة في وظيفة beforeSend - إن عنصر الإدخال الذي يتم تقديمه يستخدم علامات اقتباس مزدوجة يمكن أن تؤدي إلى قطع حرفية JavaScript.

عميل جافا سكريبت

عندما يتم تنفيذ ذلك ، يتم استدعاء الدالة beforeSend أعلاه ويضاف AntiForgeryToken تلقائيًا إلى رؤوس الطلبات.

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

مكتبة الخادم

مطلوب سمة مخصصة لمعالجة رمز مميز غير قياسي. هذا يعتمد على حل @ viggity ، لكنه يعالج آفاكس غير مزعجة بشكل صحيح. يمكن مد هذا الرمز بعيدًا في مكتبتك العامة

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

الخادم / المراقب المالي

الآن قمت فقط بتطبيق السمة على الإجراء الخاص بك. حتى أفضل يمكنك تطبيق السمة على وحدة التحكم الخاصة بك وسيتم التحقق من صحة جميع الطلبات.

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

أعلم أن هناك الكثير من الإجابات الأخرى ، لكن هذه المقالة جميلة وموجزة وتجبرك على التحقق من جميع رسائل HttpPosts الخاصة بك وليس بعضها فقط:

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

يستخدم رؤوس HTTP بدلاً من محاولة تعديل مجموعة النماذج.

الخادم

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

زبون

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

أعلم أنه كان هناك بعض الوقت منذ نشر هذا السؤال ، لكنني وجدت موردًا مفيدًا حقًا ، والذي يناقش استخدام AntiForgeryToken ويجعله أقل إزعاجًا للاستخدام. كما يوفر البرنامج المساعد jquery بسهولة بما في ذلك رمز antiforgery في المكالمات AJAX:

طلب وصفات مضادة للتزوير لـ ASP.NET MVC و AJAX

أنا لا أساهم كثيرًا ، ولكن ربما سيجد شخص ما أنه مفيد.


أنا أستخدم وظيفة ajax لتشغيل طريقة حذف (يحدث أن يكون من جدول زمني visjs ولكن هذا ليس relelvant). هذا ما أقوم به:

هذا هو بلدي 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>

كل ما أضفته هنا كان @Html.AntiForgeryToken() الرمز المميز في الصفحة

ثم في آخر أياكس بلدي اعتدت:

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

الذي يضيف قيمة الرمز المميز ، كشط قبالة الصفحة ، إلى الحقول المنشورة

قبل ذلك ، حاولت وضع القيمة في رؤوس الرسائل ولكنني حصلت على نفس الخطأ

لا تتردد في نشر التحسينات. يبدو هذا بالتأكيد نهجًا بسيطًا يمكنني فهمه


أنا استخدم وظيفة شبيبة بسيطة مثل هذا

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

نظرًا لأن كل نموذج على الصفحة سيكون له نفس القيمة للرمز ، فما عليك سوى وضع شيء مثل هذا في أهم صفحة رئيسية

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

ثم في مكالمة أجاكس الخاصة بك (تحريرها لتتناسب مع المثال الثاني الخاص بك)

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

تحسن طفيف إلى حل 360Airwalk. يؤدي هذا إلى دمج رمز Anti Forgery Token داخل وظيفة javascript ، لذلك لم يعد @ Html.AntiForgeryToken () بحاجة إلى تضمينه في كل مشاهدة.

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

كنت مجرد تنفيذ هذه المشكلة الفعلية في مشروعي الحالي. لقد فعلت ذلك لجميع من أجاكس POSTs التي تحتاج إلى مستخدم مصادق عليه.

أولاً ، قررت ربط مكالماتي بأجاكس حتى لا أكرر نفسي كثيرًا. يضمن مقتطف javascript هذا أن جميع مكالمات ajax (post) ستضيف رمز التحقق من صحة الطلب الخاص بي إلى الطلب. ملاحظة: يتم استخدام الاسم __RequestVerificationToken في إطار .Net حتى أتمكن من استخدام ميزات Anti-CSRF القياسية كما هو موضح أدناه.

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

في "طرق العرض" التي تحتاج فيها إلى الرمز المميز ليكون متاحًا لجافا سكريبت أعلاه ، استخدم فقط HTML-Helper. يمكنك في الأساس إضافة هذا الرمز في أي مكان تريده. لقد وضعتها ضمن عبارة (Request.IsAuthenticated) إذا:

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

في وحدة التحكم الخاصة بك ، استخدم آلية ASP.Net MVC Anti-CSRF القياسية. أنا فعلت هذا مثل هذا (على الرغم من أنني فعلا استخدمت الملح).

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

باستخدام Firebug أو أداة مشابهة ، يمكنك بسهولة معرفة كيف أن طلبات POST الخاصة بك الآن تحتوي على معلمة __RequestVerificationToken ملحقة.


وجدت هذه الفكرة ذكية جدا من https://gist.github.com/scottrippey/3428114 مقابل كل $ .ajax يدعو فإنه يعدل الطلب وإضافة الرمز المميز.

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

يمكنك القيام بذلك أيضا:

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

هذا هو استخدام Razor ، ولكن إذا كنت تستخدم بناء جملة WebForms يمكنك فقط استخدام <%= %> العلامات


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




antiforgerytoken