۱۳۹۰/۰۲/۳۰

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


ASP.NET به صورت پيش فرض در مقابل ارسال هر نوع تگي عكس العمل نشان مي‌دهد و پيغام خطاي يافتن خطري بالقوه را گوشزد مي‌كند. اما بين خودمان باشد، همه اين قابليت را خاموش مي‌كنند! چون در يك برنامه واقعي نياز است تا مثلا كاربران تگ html هم ارسال كنند. براي نمونه يك اديتور متني پيشرفته را درنظر بگيريد. خاموش كردن اين قابليت هم مساوي است با فراهم كردن امكان ارسال تگ‌هاي مجاز و در كنار آن بي دفاع گذاشتن برنامه در مقابل حملات XSS.
توصيه هم اين است كه همه جا از توابع مثلا HtmlEncode و موارد مشابه حتما استفاده كنيد. ولي باز هم خودمونيم ... چند نفر از شماها اينكار را مي‌كنيد؟!
بهترين كار در اين موارد وارد شدن به pipe line پردازشي ASP.NET و دستكاري آن است! اينكار هم توسط HttpModules ميسر است. به عبارتي در ادامه مي‌خواهيم ماژولي را بنويسيم كه كليه تگ‌هاي ارسالي كوئري استرينگ‌ها را پاك كرده و همچنين تگ‌هاي خطرناك موجود در مقادير ارسالي فرم‌هاي برنامه را هم به صورت خودكار حذف كند. اما هنوز اجازه بدهد تا كاربران بتوانند تگ HTML هم ارسال كنند.
مشكل! در ASP.NET مقادير ارسالي كوئري استرينگ‌ها و همچنين فرم‌ها به صورت NameValueCollection در اختيار برنامه قرار مي‌گيرند و ... خاصيت IsReadOnly اين مجموعه‌ها در حين ارسال، به صورت پيش فرض true است و همچنين غيرعمومي! يعني به همين سادگي نمي‌توان عمليات تميزكاري را روي مقادير ارسالي، پيش از مهيا شدن آن جهت استفاده در برنامه اعمال كرد. بنابراين در ابتداي كار نياز است با استفاده از قابليت Reflection‌ ، اندكي در سازوكار داخلي ASP.NET دست برد، اين خاصيت فقط خواندني غيرعمومي را براي مدت كوتاهي false كرد و سپس مقصود نهايي را اعمال نمود. پياده سازي آن‌ را در ادامه مشاهده مي‌كنيد:
using System;
using System.Collections.Specialized;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Web;
using Microsoft.Security.Application;

namespace AntiXssMdl
{
public class AntiXssModule : IHttpModule
{
private static readonly Regex _cleanAllTags = new Regex("<[^>]+>", RegexOptions.Compiled);

public void Init(HttpApplication context)
{
context.BeginRequest += CleanUpInput;
}

public void Dispose()
{ }

private static void CleanUpInput(object sender, EventArgs e)
{
HttpRequest request = ((HttpApplication)sender).Request;

if (request.QueryString.Count > 0)
{
//تميزكاري مقادير كليه كوئري استرينگ‌ها پيش از استفاده در برنامه
CleanUpAndEncode(request.QueryString, allowHtmltags: false);
}

if (request.HttpMethod == "POST")
{
//تميزكاري كليه مقادير ارسالي به سرور
if (request.Form.Count > 0)
{
CleanUpAndEncode(request.Form, allowHtmltags: true);
}
}
}

private static void CleanUpAndEncode(NameValueCollection collection, bool allowHtmltags)
{
//اندكي دستكاري در سيستم داخلي دات نت
PropertyInfo readonlyProperty = collection
.GetType()
.GetProperty("IsReadOnly",
BindingFlags.Instance | BindingFlags.NonPublic);
readonlyProperty.SetValue(collection, false, null);//IsReadOnly=false

for (int i = 0; i < collection.Count; i++)
{
if (string.IsNullOrWhiteSpace(collection[i])) continue;

if (!allowHtmltags)
{
//در حالت كوئري استرينگ دليلي براي ارسال هيچ نوع تگي وجود ندارد
collection[collection.Keys[i]] =
AntiXss.HtmlEncode(_cleanAllTags.Replace(collection[i], string.Empty));
}
else
{
//قصد تميز سازي ويوو استيت را نداريم چون در اين حالت وب فرم‌ها از كار مي‌افتند
if (collection.Keys[i].StartsWith("__VIEWSTATE")) continue;
//در ساير موارد كاربران مجازند فقط تگ‌هاي سالم را ارسال كنند و مابقي حذف مي‌شود
collection[collection.Keys[i]] = Sanitizer.GetSafeHtml(collection[i]);
}
}

readonlyProperty.SetValue(collection, true, null);//IsReadOnly=true
}
}
}

در اين كلاس از كتابخانه AntiXSS مايكروسافت استفاده شده است. آخرين نگارش آن‌را از اينجا دريافت نمائيد. نكته مهم آن متد Sanitizer.GetSafeHtml است. به كمك آن با خيال راحت مي‌توان در يك سايت، از يك اديتور متني پيشرفته استفاده كرد. كاربران هنوز مي‌توانند تگ‌هاي HTML را ارسال كنند؛ اما در اين بين هرگونه سعي در ارسال عبارات و تگ‌هاي حاوي حملات XSS پاكسازي مي‌شود.

و يك وب كانفيگ نمونه براي استفاده از آن به صورت زير مي‌تواند باشد (تنظيم شده براي IIS6 و 7):
<?xml version="1.0"?>
<configuration>
<system.web>
<pages validateRequest="false" enableEventValidation="false" />
<httpRuntime requestValidationMode="2.0" />
<compilation debug="true" targetFramework="4.0" />

<httpModules>
<add name="AntiXssModule" type="AntiXssMdl.AntiXssModule"/>
</httpModules>

</system.web>

<system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<modules>
<add name="AntiXssModule" type="AntiXssMdl.AntiXssModule"/>
</modules>
</system.webServer>
</configuration>

براي مثال به تصوير زير دقت كنيد. ماژول فوق، فقط تگ‌هاي سبز رنگ را (حين ارسال به سرور) مجاز دانسته، اسكريپت ذيل لينك را كلا حذف كرده و تگ‌هاي موجود در كوئري استرينگ را هم نهايتا (زمانيكه در اختيار برنامه قرار مي‌گيرد) حذف خواهد كرد.


دريافت مثال