۱۳۹۱/۰۱/۲۹

ASP.NET MVC #17


فيلترهاي امنيتي ASP.NET MVC

ASP.NET MVC به همراه تعدادي فيلتر امنيتي توكار است كه در اين قسمت به بررسي آ‌ن‌ها خواهيم پرداخت.


بررسي اعتبار درخواست (Request Validation) در ASP.NET MVC

ASP.NET MVC امكان ارسال اطلاعاتي را كه داراي تگ‌هاي HTML باشند، نمي‌دهد. اين قابليت به صورت پيش فرض فعال است و جلوي ارسال انواع و اقسام اطلاعاتي كه ممكن است سبب بروز حملات XSS Cross site scripting attacks شود را مي‌گيرد. نمونه‌اي از خطاي نمايش داده:

A potentially dangerous Request.Form value was detected from the client (Html="<a>"). 

بنابراين تصميم گرفته شده صحيح است؛ اما ممكن است در قسمتي از سايت نياز باشد تا كاربران از يك ويرايشگر متني پيشرفته استفاده كنند. خروجي اين نوع ويرايشگرها هم HTML است. در اين حالت مي‌توان صرفا براي متدي خاص امكانات Request Validation را به كمك ويژگي ValidateInput غيرفعال كرد:

[HttpPost]
[ValidateInput(false)]
public ActionResult CreateBlogPost(BlogPost post)

از ASP.NET MVC 3.0 به بعد راه حل بهتري به كمك ويژگي AllowHtml معرفي شده است. غيرفعال كردن ValidateInput ‌ايي كه معرفي شد، بر روي تمام خواص شيء BlogPost اعمال مي‌شود. اما اگر فقط بخواهيم كه مثلا خاصيت Text آن از مكانيزم بررسي اعتبار درخواست خارج شود، بهتر است ديگر از ويژگي ValidateInput استفاده نشده و به نحو زير عمل گردد:

using System;
using System.Web.Mvc;

namespace MvcApplication14.Models
{
    public class BlogPost
    {
        public int Id { set; get; }
        public DateTime AddDate { set; get; }
        public string Title { set; get; }

        [AllowHtml]
        public string Text { set; get; }
    }
}

در اينجا فقط خاصيت Text مجاز به دريافت محتواي HTML ايي خواهد بود. اما خاصيت Title چنين مجوزي را ندارد. همچنين ديگر نيازي به استفاده از ويژگي ValidateInput غيرفعال شده نيز نخواهد بود.
به علاوه همانطور كه در قسمت‌هاي قبل نيز ذكر شد، خروجي Razor به صورت پيش فرض Html encoded است مگر اينكه صريحا آن‌را تبديل به HTML كنيم (مثلا استفاده از متد Html.Raw). به عبارتي خروجي Razor در حالت پيش فرض در مقابل حملات XSS مقاوم است مگر اينكه آگاهانه بخواهيم آن‌را غيرفعال كنيم.

مطلب تكميلي
مقابله با XSS ؛ يكبار براي هميشه!



فيلتر RequireHttps

به كمك ويژگي يا فيلتر RequireHttps، تمام درخواست‌هاي رسيده به يك متد خاص بايد از طريق HTTPS انجام شوند و حتي اگر شخصي سعي به استفاده از پروتكل HTTP معمولي كند، به صورت خودكار به HTTPS هدايت خواهد شد:

[RequireHttps]
public ActionResult LogOn() 
{ 
} 


فيلتر ValidateAntiForgeryToken

نوع ديگري از حملات كه بايد در برنامه‌هاي وب به آن‌ها دقت داشت به نام CSRF يا Cross site request forgery معروف هستند.
براي مثال فرض كنيد كاربري قبل از اينكه بتواند در سايت شما كار خاصي را انجام دهد، نياز به اعتبار سنجي داشته باشد. پس از لاگين شخص و ايجاد كوكي و سشن معتبر، همين شخص به سايت ديگري مراجعه مي‌كند كه در آن مهاجمي بر اساس وضعيت جاري اعتبار سنجي او مثلا لينك حذف كاربري يا افزودن اطلاعات جديدي را به برنامه ارائه مي‌دهد. چون سشن شخص و كوكي مرتبط به سايت اول هنوز معتبر هستند و شخص سايت را نبسته است، «احتمال» اجرا شدن درخواست مهاجم بالا است (خصوصا اگر از مرورگرهاي قديمي استفاده كند).
بنابراين نياز است بررسي شود آيا درخواست رسيده واقعا از طريق فرم‌هاي برنامه ما صادر شده است يا اينكه شخصي از طريق سايت ديگري اقدام به جعل درخواست‌ها كرده است.
براي مقابله با اين نوع خطاها ابتدا بايد داخل فرم‌هاي برنامه از متد Html.AntiForgeryToken استفاده كرد. كار اين متد ايجاد يك فيلد مخفي با مقداري منحصربفرد بر اساس اطلاعات سشن جاري كاربر است، به علاوه ارسال يك كوكي خودكار تا بتوان از تطابق اطلاعات اطمينان حاصل كرد:

@using (Html.BeginForm()) {
    @Html.AntiForgeryToken()

در مرحله بعد بايد فيلتر ValidateAntiForgeryToken جهت بررسي مقدار token دريافتي به متد ثبت اطلاعات اضافه شود:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateBlogPost(BlogPost post)

در اينجا مقدار دريافتي از فيلد مخفي فرم :

<input name="__RequestVerificationToken" type="hidden" value="C0iPfy/3T....=" />

با مقدار موجود در كوكي سايت بررسي و تطابق داده خواهند شد. اگر اين مقادير تطابق نداشته باشند، يك استثنا صادر شده و از پردازش اطلاعات رسيده جلوگيري مي‌شود.
علاوه بر اين‌ها بهتر است حين استفاده از متد و فيلتر ياد شده، از يك salt مجزا نيز به ازاي هر فرم، استفاده شود:

[ValidateAntiForgeryToken(Salt="1234")]

@Html.AntiForgeryToken(salt:"1234")

به اين ترتيب tokenهاي توليد شده در فرم‌هاي مختلف سايت يكسان نخواهند بود.
به علاوه بايد دقت داشت كه ValidateAntiForgeryToken فقط با فعال بودن كوكي‌ها در مرورگر كاربر كار مي‌كند و اگر كاربري پذيرش كوكي‌ها را غيرفعال كرده باشد، قادر به ارسال اطلاعاتي به برنامه نخواهد بود. همچنين اين فيلتر تنها در حالت HttpPost قابل استفاده است. اين مورد هم در قسمت‌هاي قبل تاكيد گرديد كه براي مثال بهتر است بجاي داشتن لينك delete در برنامه كه با HttpGet ساده كار مي‌كند،‌ آن‌را تبديل به HttpPost نمود تا ميزان امنيت برنامه بهبود يابد. از HttpGet فقط براي گزارشگيري و خواندن اطلاعات از برنامه استفاده كنيد و نه ثبت اطلاعات.
بنابراين استفاده از AntiForgeryToken را به چك ليست اجباري توليد تمام فرم‌هاي برنامه اضافه نمائيد.

مطلب مشابه
Anti CSRF module for ASP.NET



فيلتر سفارشي بررسي Referrer

يكي ديگر از روش‌هاي مقابله با CSRF، بررسي اطلاعات هدر درخواست ارسالي است. اگر اطلاعات Referrer آن با دومين جاري تطابق نداشت، به معناي مشكل دار بودن درخواست رسيده است. فيلتر سفارشي زير مي‌تواند نمونه‌اي باشد جهت نمايش نحوه بررسي UrlReferrer درخواست رسيده:

using System.Web.Mvc;

namespace MvcApplication14.CustomFilter
{
    public class CheckReferrerAttribute : AuthorizeAttribute
    {
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            if (filterContext.HttpContext != null)
            {
                if (filterContext.HttpContext.Request.UrlReferrer == null)
                    throw new System.Web.HttpException("Invalid submission");

                if (filterContext.HttpContext.Request.UrlReferrer.Host != "mysite.com")
                    throw new System.Web.HttpException("This form wasn't submitted from this site!");
            }

            base.OnAuthorization(filterContext);
        }
    }
}

و براي استفاده از آن:
[HttpPost]
[CheckReferrer]
[ValidateAntiForgeryToken]
public ActionResult DeleteTask(int id)


نكته‌اي امنيتي در مورد آپلود فايل‌ها در ASP.NET

هر جايي كه كاربر بتواند فايلي را به سرور شما آپلود كند، مشكلات امنيتي هم از همانجا شروع خواهند شد. مثلا در متد Upload قسمت 11 اين سري، منعي در آپلود انواع فايل‌ها نيست و كاربر مي‌تواند انواع و اقسام شل‌ها را جهت تحت كنترل گرفتن سايت و سرور آپلود و اجرا كند. راه حل چيست؟
از همان روش امنيتي مورد استفاده توسط تيم ASP.NET MVC استفاده مي‌كنيم. فايل web.config قرار گرفته در پوشه Views را باز كنيد (نه فايل وب كانفيگ ريشه اصلي سايت‌را). چنين تنظيمي را مي‌توان مشاهده كرد:
براي IIS6 :

<system.web>
    <httpHandlers>
      <add path="*" verb="*" type="System.Web.HttpNotFoundHandler"/>
    </httpHandlers>
</system.web>
براي IIS7 :
<system.webServer>
    <handlers>
      <remove name="BlockViewHandler"/>
      <add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
    </handlers>
  </system.webServer>


تنظيم فوق، موتور اجرايي ASP.NET را در اين پوشه خاص از كار مي‌اندازد. به عبارتي اگر شخصي مسير يك فايل aspx يا cshtml يا هر فايل قرار گرفته در پوشه Views را مستقيما در مرورگر خود وارد كند، با پيغام HttpNotFound مواجه خواهد شد.
اين روش هم با ASP.NET Web forms سازگار است و هم با ASP.NET MVC؛ چون مرتبط است به موتور اجرايي ASP.NET كه هر دوي اين فريم ورك‌ها برفراز آن معنا پيدا مي‌كنند.
بنابراين در پوشه فايل‌هاي آپلودي به سرور خود يك web.config را با محتواي فوق ايجاد كنيد (و فقط بايد مواظب باشيد كه اين فايل حين آپلود فايل‌هاي جديد، overwrite نشود. مهم!). به اين ترتيب اين مسير ديگر از طريق مرورگر قابل دسترسي نخواهد بود (با هر محتوايي). سپس براي ارائه فايل‌هاي آپلودي به كاربران از روش زير استفاده كنيد:

public ActionResult Download()
{
      return File(Server.MapPath("~/Myfiles/test.txt"), "text/plain");
}

مزيت مهم روش ذكر شده اين است كه كاربران مجاز به آپلود هر نوع فايلي خواهند بود و نيازي نيست ليست سياه تهيه كنيد كه مثلا فايل‌هايي با پسوند‌هاي خاص آپلود نشوند (كه در اين بين ممكن است ليست سياه شما كامل نباشد ...).




علاوه بر تمام فيلترهاي امنيتي كه تاكنون بررسي شدند،‌ فيلتر ديگري نيز به نام Authorize وجود دارد كه در قسمت‌هاي بعدي بررسي خواهد شد.