ارتقا HTML برای ایجاد ورودی های قدرتمند درNET .

یکشنبه 3 اردیبهشت 1396

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

ارتقا HTML برای ایجاد ورودی های  قدرتمند درNET .

ما به تازگی روی بروز رسانی یک برنامه قدیمی در 4mvc که شامل فرم رایگان ورود متن بود کار کردیم.وقتی سایت جدیدمان را راه اندازی کردیم، رویکرد اولیه ما این بود که به هیچ یک از ورودی های قدرتمند به جز یک قالب متن ساده که به دستورات ساده HTML برای bold کردن متن یا ایجاد لیست و... ارجاع می دهد و یا رفتن به خط جدید و پارگراف جدید را اعمال می کند، اجازه ورود ندهیم. اینکه ما در برنامه های خودمان با ورودی های چند خطی چه می کنیم که با کمترین پیچیدگی و تلاش خیلی خوب کار می کند، کارسختی نیست که در ادامه بررسی خواهیم کرد.

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

در این زمینه بحث های زیادی در سایت  StackOverFlow وسایت های دیگر وجود دارد.اما بعد از مطالعه اکثر این راه حل ها ، راهی پیدا نکردیم تا پاسخگوی حتی مشابه نیاز ما باشد. ما به طور مشخص می خواهیم به همه ی ورودی های HTML به جز اسکریپت ها اجازه ورود دهیم . نیاز داریم که یک  مقدار CSS و عکس بارگذاری شود، لینک ها فعال باشند و چیزهایی از این قبیل، درحالیکه HTML که مجاز به  ارسال همچین عملیاتی است درحقیقت وسعت زیادی از HTML(4)  را پوشش می دهد.

بسیاری از راه حل های XSS راه حل های پیشگیرانه بودند که تهاجمی هم به نظر می رسیدند و خروجی های غیرقابل استفاده ارائه می دادند زیرا آنها جلوی هر محتوای خارجی را می گرفتند.

به طور خلاصه ما نیاز به یک راه حل سفارشی داریم. بهترین راه حل برای این امر استفاده از یک تجزیه گر HTML به نظر می رسد( در این موردHtml Agility Pack) که بین همه ی کدهای HTML   اجرا شود و همه ی تگ های blacklisted  و تعدادی از خصوصیاتی که قصد تزریق جاوا اسکریپت را دارند حذف نماید.

بحث های زیادی پیرامون مقابله از طریق blackliste وجود دارد. لیست سفید نیز دراین بحث ها ذکر شده که در سناریوهای ساده منطقی و اجرایی است . در این راه شما در صورت نیاز به صورت دستی به ورودی های HTML که می خواهید ، حق دسترسی می دهید. اما وقتی شما نیاز دارید تا به آرایه بزرگی از عملگرهای HTML حق دسترسی دهید، مدیریت یک blacklist  آسانتر از مدیریت حجم وسیعی از المان ها و خصوصیاتی است که اجازه ورود دارند. لیست سفید همچنین در HTML5 با اضافه شدن تگ های جدیدی  که عموما نسخه XSS روی آنها تاثیرمستقیمی ندارد بسیار پیچیده تر شده است. لیست سفید به تنهایی بر اساس یک سری المان ها و خصوصیاتی است که بسیاری از موارد حاشیه ای را در بر نمی گیرد، طوری که حتی با استفاده از لیست سفید وجود یک منطق سفارشی برای بررسی موارد حاشیه ای لازم و ضروری است.

کتابخانه امنیت وب مایکروسافت(AntiXSS)

مایکروسافت یک نوع رمزنگاری HTML و کتابخانه بهسازی Microsoft Web Protection Library  (AntiXSS  سابق)را دارد که براساس CodePlex است. این کتابخانه توابع پیچیده ای را برای رمزنگاری لیست سفید و بهسازی آن تهیه می کند .ابتدا فکر کردیم کلاس بهسازی و مقدار ثابت آن یک فوت و فن است ولی بعد از بررسی های انجام شده متوجه شدیم که این کتابخانه ما را بسیار محدود می کند. به طور مشخص کلاسی که عکس ها و لینک هایی که فقط اجرا کننده HTML هستند و وضعیت واقعی کاربران را پوشش نمی دهد، کاملا بی فایده است.

یک مثال برای روشن شدن این مطلب که چرا این کتابخانه مفید نیست برای شما می زنیم .این یک قطعه کد HTML ساده شامل اسکریپت و عکس و تگ های لینک است . انتظار می رود که اسکریپت ها حذف شوند و هرچیزی به غیر از آن دست نخورده باقی بماند.

کدهای HML

var value = "<b>هی</b> <script>alert('سلام')</script> ما اومدیم. سایت  " + 
            "<a href='https://barnamenevisan.org/'>برنامه نویسان </a> را ببینید. " +            "<img src='https://barnamenevisan.org//images/new.gif' /> " ;

و کدهایی برای ارتقا آن با کلاس بهسازی AntiXSS

@Html.Raw(Microsoft.Security.Application.Sanitizer.GetSafeHtmlFragment(value))

جمله تولید شده قابل استفاده نیست.

ما اومدیم سایت <a> برنامه نویسان>  </aرا ببینید.

علاوه بر اینکه تگ <script> را پاک کرده (که خوب است) ، href  لینک و تگ تصویر را نیز کاملا پاک کرده است (این بد است). ممکن است بعضی مواقع این کار خوب باشد ولی بیشتر وقتها نتیجه مطلوب حاصل نمی شود. در حالیکه لینک ها ممکن است به یک منبع جاوا اسکریپت اشاره کنند و تصاویر هم می تواند اطلاعات را به سرور broadcast کنند ، این کتابخانه بدون در نظر گرفتن وضعیت همه را محدود می کند . چون ما راهی برای سفارشی سازی لیست سفید پیدا نکردیم و این کتابخانه روی CodePlex متن باز نیست این کتابخانه برای ما غیر قابل استفاده است .

استفاده از Html Agility Pack برای تجزیه HTML

بعد از تحقیقات گسترده به این نتیجه رسیدیم که بهترین رویکرد برای راه حل سفارشی این است که از تجزیه گر HTML استفاده کنیم و اینکه قطعه کد یا سندی که سعی در وارد کردن آن داریم را بازرسی کنیم.  Html Agility Pack یک کتابخانه قدرتمند تجزیه گر HTML است که شما می توانید در آن از کدهای .NET خود استفاده کنید. این کتابخانه یک مدل  HTML DOM ساده و قابل تجزیه برای همه اسناد HTML و یا قطعات HTML که شما روی هر المان اجازه دسترسی می دهید ،تولید می کند .

تجزیه HTML براساس لیست سیاه (BlackList) برای خالی کردن کد XSS

برای ارتقا HTML فرآیند به این صورت است که بین کدهای HTML می گردیم و هممه ی المان ها و خصوصیات را  براساس لیست سیاه چک می کنیم . در اینجا بحثی که وجود دارد این است که یک لیست سفید که فقط به برخی آیتم ها اجازه ی ورود می دهد بهتر است یا یک لیست سیاه که فقط جلوی برخی آیتم ها را می گیرد. اگرچه لیست سفید امنیت بالاتری دارد ولی نیاز به پیکربندی بیشتری نیز دارد. در مورد HTML5 یک لیست سفید می تواند خیلی گسترده باشد. برای اینکه بدانیم چه چیزی نیاز داریم ،کافی است مطمئن شویم که هیچ کد جاوااسکریپتی نباید اجرا شود، بنابراین یک لیست سیاه شامل تگ های آشکار <script> به اضافه هر تگی که محتوای خارجی را بارگذاری می کند مانند <iframe>, <object>, <embed>  <link> ,و ...  . تگ فرم <form> هم نباید به جای دیگری پست شود . تگ <head> و <meta> تا وقتی که کد HTML را پست نکردیم نباید به جایی ارسال شوند . یک سری منطق های داخلی هم برای منع کردن بعضی از خصوصیاتی که به کدهای جاوااسکریپت به CSS ها ی خارجی اشاره می کند وجود دارد .اینها تگ های لیست سیاه پیش فرض هستند ولی قابلیت سفارشی سازی دارد و شما می توانید تگ های دیگری را به آن اضافه کنید.

using System.Collections.Generic;
using System.IO;
using System.Xml;
using HtmlAgilityPack;

namespace Westwind.Web.Utilities
{
    public class HtmlSanitizer
    {

        public HashSet<string> BlackList = new HashSet<string>() 
        {
                { "script" },
                { "iframe" },
                { "form" },
                { "object" },
                { "embed" },
                { "link" },                
                { "head" },
                { "meta" }
        };

        /// <summary>
        /// Cleans up an HTML string and removes HTML tags in blacklist
        /// </summary>
        /// <param name="html"></param>
        /// <returns></returns>
        public static string SanitizeHtml(string html, params string[] blackList)
        {
            var sanitizer = new HtmlSanitizer();
            if (blackList != null && blackList.Length > 0)
            {
                sanitizer.BlackList.Clear();
                foreach (string item in blackList)
                    sanitizer.BlackList.Add(item);
            }
            return sanitizer.Sanitize(html);
        }

        /// <summary>
        /// Cleans up an HTML string by removing elements
        /// on the blacklist and all elements that start
        /// with onXXX .
        /// </summary>
        /// <param name="html"></param>
        /// <returns></returns>
        public string Sanitize(string html)
        {
            var doc = new HtmlDocument();
            
            doc.LoadHtml(html);            
            SanitizeHtmlNode(doc.DocumentNode);            
            
            //return doc.DocumentNode.WriteTo();

            string output = null;

            // Use an XmlTextWriter to create self-closing tags
            using (StringWriter sw = new StringWriter())
            {
                XmlWriter writer = new XmlTextWriter(sw);
                doc.DocumentNode.WriteTo(writer);
                output = sw.ToString();

                // strip off XML doc header
                if (!string.IsNullOrEmpty(output))
                {
                    int at = output.IndexOf("?>");
                    output = output.Substring(at + 2);
                }

                writer.Close();
            }
            doc = null;

            return output;
        }

        private void SanitizeHtmlNode(HtmlNode node)
        {
            if (node.NodeType == HtmlNodeType.Element)
            {
                // check for blacklist items and remove
                if (BlackList.Contains(node.Name))
                {
                    node.Remove();
                    return;
                }

                // remove CSS Expressions and embedded script links
                if (node.Name == "style")
                {
                    if (string.IsNullOrEmpty(node.InnerText))
                    {
                        if (node.InnerHtml.Contains("expression") || node.InnerHtml.Contains("javascript:"))
                            node.ParentNode.RemoveChild(node);
                    }
                }

                // remove script attributes
                if (node.HasAttributes)
                {
                    for (int i = node.Attributes.Count - 1; i >= 0; i--)
                    {
                        HtmlAttribute currentAttribute = node.Attributes[i];
                 
                        var attr = currentAttribute.Name.ToLower();
                        var val = currentAttribute.Value.ToLower();
                        
                        span style="background: white; color: green">// remove event handlers
                        if (attr.StartsWith("on"))
                            node.Attributes.Remove(currentAttribute);
                        
                        // remove script links
                        else if (
                                 //(attr == "href" || attr== "src" || attr == "dynsrc" || attr == "lowsrc") &&
                                 val != null &&
                                 val.Contains("javascript:"))
                            node.Attributes.Remove(currentAttribute);
                        
                        // Remove CSS Expressions
                        else if (attr == "style" && 
                                 val != null &&
                                 val.Contains("expression") || val.Contains("javascript:") || val.Contains("vbscript:"))
                            node.Attributes.Remove(currentAttribute);
                    }
                }
            }

            // Look through child nodes recursively
            if (node.HasChildNodes)
            {
                for (int i = node.ChildNodes.Count - 1; i >= 0; i--)
                {
                    SanitizeHtmlNode(node.ChildNodes[i]);
                }
            }
        }
    }
}

نکنه: برای شروع کار به این نکته توجه کنید که برای تجزیه و تحلیل کدهای خودتان اگر نیاز به ورودی و خروجی از جایی غیر از html خودتان ندارید می توانید دسترسی کدها را سختگیرانه تر انجام دهید و تمام src ها و تمام لینکها و تصاویر HTML را محدود کنید. شما همینطور می توانید لینکها را برای عدم دسترسی به  url های خارجی چک کنید. کدها به اندازه کافی راحت هستند که بتوانید آنهارا شخصی سازی کنید. اگر می خواهید به جای خاصی لینک دهید می توانید از لیست سفید استفاده کنید . کد بالا نیمه عمومی است و به تمام ویژگی های HTML به جز محتویات اسکریپتی اجازه ورود می دهد. متد Sanitize  بین کدهای html می گردد و به صورت بازگشتی به تمام کدهای فرزند می رود تا سند HTML را کاملا بازرسی کند . توجه کنید که کد بالا از  XmlTextWriter برای گرفتن خروجی استفاده کرده است . این به این دلیل است که XHTML تگ پایانی خود را نگه می دارد در صورتیکه بقیه این کار را انجام نمی دهند.

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

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

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

اگر ما از مرورگر های امن استفاده کنیم و هر اسکریپت کوچکی که در صفحات وب می بینیم را اجرا نکنیم تجربه بهتری از استفاده از وب خواهیم داشت. هرچقدر ما زمان بیشتری را برای ساخت یک برنامه امن بگذاریم هکرها نیاز به زمان بیشتری برای هک کردن برنامه ما دارند.

آموزش سی شارپ

برنامه نویسان

نویسنده 3355 مقاله در برنامه نویسان
  • C#.net
  • 1k بازدید
  • 4 تشکر

کاربرانی که از نویسنده این مقاله تشکر کرده اند

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید