امن سازی درخواست‌های ای‌جکسی برنامه‌های ASP.NET MVC 5.x در مقابل حملات CSRF
اندازه‌ی قلم متن
تخمین مدت زمان مطالعه‌ی مطلب: دو دقیقه

طی مقاله چک لیست تولید برنامه Asp.net mvc و بررسی امنیتی ای‌جکس هنگام استفاده در مورد چک لیست امنیتی سایت سرفصل‌های مهم عنوان و بررسی شده است که یکی از موارد، مقاوم ساختن وب اپلیکشن در برابر حملات CSRF می‌باشد. اینگونه حملات بر پایه این استراتژی شکل می‌گیرند که با ارسال درخواستی به نیابت از سمت سیستم/مرورگر کاربر تایید هویت شده، سایت مقصد را مجبور به انجام عملی کند. برای مثال اگر شما در سایت a.com یک کاربر تایید شده باشید و هم اکنون در سایت فوق نیز لاگین باشید، مهاجم با ارسال یک برنامه/صفحه یا موارد مشابه و در قالب src یک عکس یا با ترغیب شما با کلیک بر روی یک لینک با href آلوده یا موارد مشابه، از سمت مرورگر شما درخواستی را به سمت سایت a.com ارسال می‌کند .

این درخواست ممکن است شامل حذف اطلاعات، تغییر مشخصات، پرداخت هزینه یا موارد مشابه باشد. جهت مقابله با این حمله، یکی از موارد مهم، استفاده همیشگی از Html.AntiForgeryToken() در تمامی فرم‌های ورود اطلاعات است. همچنین استفاده همیشگی از متد Post و بررسی تایید مبدا درخواست‌های ای‌جکسی، بررسی http referrer ، محدود کردن طول عمر کوکی، استفاده از کپچهای قوی مانند کپچای گوگل می‌تواند تا حد زیادی وب اپلیکیشن را در مورد اینگونه حملات، مصون کند.

در این بین یکی از موارد دیگر، اضافه کردن AntiForgeryToken به درخواست‌های ا‌ی‌جکسی سایت می‌باشد. جهت حصول این منظور، راه‌های مختلفی موجود است. یکی از راه حل‌ها استفاده از یک هلپر جهت تولید توکن مورد نظر است.

ساختار هلپر مورد نظر به شرح زیر است :

public static class AntiForgeryToken
{
  public static MvcHtmlString AntiForgeryTokenForAjaxPost(this HtmlHelper helper)
  {
    var antiForgeryInputTag = helper.AntiForgeryToken().ToString();
    // Above gets the following: <input name="__RequestVerificationToken" type="hidden" value="some value" />
    var removedStart = antiForgeryInputTag.Replace(@"<input name=""__RequestVerificationToken"" type=""hidden"" value=""", "");
    var tokenValue = removedStart.Replace(@""" />", "");
    if (antiForgeryInputTag == removedStart || removedStart == tokenValue)
       throw new InvalidOperationException("Oops! The Html.AntiForgeryToken() method seems to return something I did not expect.");
    return new MvcHtmlString($@"{"__RequestVerificationToken"}:""{tokenValue}""");
  }
}
کار آن حذف فیلد مخفی این توکن و درج آن به صورت یک شیء جاوا اسکریپتی است. 

در مرحله بعد طبق الگوی زیر، درخواست ا‌ی‌جکسی به همراه توکن تولید شده و به کنترلر ارسال خواهد شد:
function AddToCart(pid) {
   $.ajax({
     url: '@Url.Action("AddToBasket","Shop")',
     data: { 'pid': pid,@Html.AntiForgeryTokenForAjaxPost()  },
     type: 'post',
     success:function(e) {
       //do something
     }
   });
}

در مرحله آخر، باید کنترلر مورد نظر شامل ویژگی‌های [HttpPost] [ValidateAntiForgeryToken]  باشد تا صحت توکن تولیدی را بررسی کند و در صورت نامعتبر بودن، از اجرای دستورات جلوگیری گردد.
  • #
    ‫۶ سال و ۱۱ ماه قبل، جمعه ۲۸ مهر ۱۳۹۶، ساعت ۱۲:۳۵
    نکته : اگر از چنین هلپری در NetCore استفاده می‌کنید با توجه به موجود نبود mvchtmlstring  در نسخه جدید می‌تواند از HtmlString بهره ببرید.
    • #
      ‫۶ سال و ۱۱ ماه قبل، جمعه ۲۸ مهر ۱۳۹۶، ساعت ۱۲:۴۸
      این روش عمومی‌تر است:
      function addToken(data) {
         data.__RequestVerificationToken = $("input[name=__RequestVerificationToken]").val();
         return data;
      }
      برای
      $.ajax({
         // .....
         data: addToken({ postId: postId }), // اضافه کردن توکن
         dataType: "html", // نوع داده مهم است
         // .....
      });
      که معادل قطعه کد زیر هست:
       data: { "pid": pid, "__RequestVerificationToken": $('[name=__RequestVerificationToken]').val()  },
      البته به نظر در این حالت "dataType: "html باشد تا کار کند.
      حالت سراسری آن هم تحت نظر قرار دادن رخ‌داد مخصوص ارسال‌های Ajax ای هست (نوشتن یک interceptor):
      $(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);
                          }
                      }
                  });
              });
      • #
        ‫۶ سال و ۱۱ ماه قبل، جمعه ۲۸ مهر ۱۳۹۶، ساعت ۱۳:۵۵
        توی این قسمت 
        function addToken(data) {
           data.__RequestVerificationToken = $("input[name=__RequestVerificationToken]").val();
           return data;
        }
        کجا 
          data: addToken ( { postId: postId } ) , // اضافه کردن توکن
          رو اضافه میکنه ؟ (postId)
          • #
            ‫۶ سال و ۱۱ ماه قبل، جمعه ۲۸ مهر ۱۳۹۶، ساعت ۱۴:۲۲
            ممنونم. مطلب رو خوندم. ولی باز برام سواله، در کد شما postId در کجای تابع addToken  مقدارش ست میشه. آیا نباید به این شکل باشه:
            function addToken(data) {
               data.__RequestVerificationToken = $("input[name=__RequestVerificationToken]").val();
               data.postId=postId;
               return data;
            }
            • #
              ‫۶ سال و ۱۱ ماه قبل، جمعه ۲۸ مهر ۱۳۹۶، ساعت ۱۴:۲۷
              مقدارش قبلا تنظیم شده‌است. data ایی که به اون ارسال شده حاوی postId هست ({} یعنی یک شیء حاوی اطلاعات و خواص جاوا اسکریپتی). در اینجا فقط به صورت پویا یک خاصیت جدید به آن اضافه می‌شود. جاوا اسکریپت یک زبان dynamic هست. مثل واژه‌ی کلیدی dynamic در #C که بعدش می‌تونید خواصی را به دلخواه به این نوع اشیاء اضافه کنید. همان توضیحات مطلبی که لینک دادم.
  • #
    ‫۶ سال و ۱۱ ماه قبل، سه‌شنبه ۲ آبان ۱۳۹۶، ساعت ۱۴:۵۶
    مباحث تکمیلی  در مورد AntiForgery و نحوه پیاده سازی به صورت عمومی و همیشگی در Asp.net Core