مطالب
مقابله با 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>

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

دریافت نسخه جدید و نهایی این مثال
نظرات مطالب
افزودن و اعتبارسنجی خودکار Anti-Forgery Tokens در برنامه‌های Angular مبتنی بر ASP.NET Core
- برای اولین باری که برنامه بارگذاری می‌شود، یک anti-forgery token تولید خواهد شد. پس از لاگین، این توکن به اطلاعات شخص متصل می‌شود. در حین logout دیگر این توکن قابلیت استفاده‌ی مجدد را ندارد و خطای فوق را دریافت می‌کنید. به همین جهت اگر logout به صورت POST انجام می‌شود، آن‌را تبدیل به GET کنید؛ از این جهت که HttpClient نگارش 4.3 به بعد اگر یکی از شرایط زیر برقرار باشد (درخواست‌های از نوع GET یا HEAD و یا آدرس‌های مطلق)، این هدر را ارسال نمی‌کند و همچنین AutoValidateAntiforgeryTokenAttribute سمت سرور هم به درخواست‌های از نوع GET واکنش نشان نمی‌دهد:
const lcUrl = request.url.toLowerCase();
if (request.method === "GET" || request.method === "HEAD" ||
          lcUrl.startsWith("http://") || lcUrl.startsWith("https://")) {
          // skip
}

چند نکته‌ی تکمیلی:
- برای یک چنین حالت‌هایی، خصوصا حالت آدرس‌های مطلق (برنامه در یک زیر پوشه اجرا شود)، نیاز است یک interceptor سفارشی ایجاد کرد.
- سفارشی سازی نام کوکی و هدر در HttpClient توسط HttpClientXsrfModule انجام می‌شود:
 imports: [   HttpClientModule,  
   HttpClientXsrfModule.withConfig({
     cookieName: 'My-Xsrf-Cookie',
     headerName: 'My-Xsrf-Header'
   }) ]
مطالب
استفاده از Froala WYSIWYG Editor در ASP.NET
چندی قبل، معرفی ادیتور سبک وزن و مناسبی را تحت عنوان RedActor، در این سایت ملاحظه کردید. زمانیکه این‌کار انجام شد، این ادیتور هم رایگان بود و هم سورس آخرین نگارش آن به سادگی در دسترس. بعد از مدتی، هر دو ویژگی یاد شده‌ی RedActor حذف شدند. پس از آن ادیتور مدرن و بسیار مناسب دیگری به نام Froala منتشر شد که هرچند نگارش‌های تجاری هم دارد، اما سورس آخرین نگارش آن برای عموم قابل دریافت است. در ادامه مروری خواهیم داشت بر نحوه‌ی یکپارچه سازی آن با ASP.NET MVC و همچنین ASP.NET Web forms.


دریافت آخرین نگارش Froala WYSIWYG Editor
برای دریافت فایل‌های آخرین نگارش این ادیتور وب می‌توانید به سایت آن، قسمت دریافت فایل‌ها مراجعه نمائید.
http://editor.froala.com/download
و یا به این آدرس مراجعه کنید:
https://github.com/froala/wysiwyg-editor/releases 


ساختار پروژه و نحوه‌ی کپی فایل‌های آن
در هر دو مثالی که فایل‌های آن‌را از انتهای بحث می‌توانید دریافت کنید، این ساختار رعایت شده است:


فایل‌های CSS و فونت‌های آن، در پوشه‌ی Content قرار گرفته‌اند.
فایل‌های اسکریپت و زبان آن (که دارای زبان فارسی هم هست) در پوشه‌ی Scripts کپی شده‌اند.

یک نکته
فایل font-awesome.css را نیاز است کمی اصلاح کنید. مسیر پوشه‌ی فونت‌های آن اکنون با fonts شروع می‌شود.


تنظیمات اولیه

تفاوتی نمی‌کند که از وب فرم‌ها استفاده می‌کنید یا MVC، نحوه‌ی تعریف و افزودن پیش نیازهای این ادیتور به نحو ذیل است:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>

    <link href="Content/font-awesome.css" rel="stylesheet" />
    <link href="Content/froala_editor.css" rel="stylesheet" />

    <script src="Scripts/jquery-1.10.2.min.js"></script>
    <script src="Scripts/froala_editor.min.js"></script>
    <script src="Scripts/langs/fa.js"></script>
</head>
<body>
    <form id="form1" runat="server">
    </form>
</body>
</html>
دو فایل CSS دارد (آیکن‌های آن و همچنین شیوه نامه‌ی اصلی ادیتور) به همراه سه فایل JS (جی‌کوئری، ادیتور و فایل زبان فارسی آن) که باید در فایل master یا layout سایت اضافه شوند.


استفاده از Froala WYSIWYG Editor در ASP.NET MVC

در ادامه نحوه‌ی فعال سازی ادیتور وب Froala را در یک View برنامه‌های ASP.NET MVC ملاحظه می‌کنید:
@{
    ViewBag.Title = "Index";
}

<style type="text/css">
    /*تنظیم فونت پیش فرض ادیتور*/
    .froala-element {
    }
</style>

@using (Html.BeginForm(actionName: "Index", controllerName: "Home"))
{
    @Html.TextArea(name: "Editor1")
    <input type="submit" value="ارسال" />
}

@section Scripts
{
    <script type="text/javascript">
        $(function () {
            $('#Editor1').editable({
                buttons: ["bold", "italic", "underline", "strikeThrough", "fontFamily",
                    "fontSize", "color", "formatBlock", "align", "insertOrderedList",
                    "insertUnorderedList", "outdent", "indent", "selectAll", "createLink",
                    "insertImage", "insertVideo", "undo", "redo", "html", "save", "inserthorizontalrule"],
                inlineMode: false,
                inverseSkin: true,
                preloaderSrc: '@Url.Content("~/Content/img/preloader.gif")',
                allowedImageTypes: ["jpeg", "jpg", "png"],
                height: 300,
                language: "fa",
                direction: "rtl",
                fontList: ["Tahoma, Geneva", "Arial, Helvetica", "Impact, Charcoal"],
                autosave: true,
                autosaveInterval: 2500,
                saveURL: '@Url.Action("FroalaAutoSave", "Home")',
                saveParams: { postId: "123" },
                spellcheck: true,
                plainPaste: true,
                imageButtons: ["removeImage", "replaceImage", "linkImage"],
                borderColor: '#00008b',
                imageUploadURL: '@Url.Action("FroalaUploadImage", "Home")',
                imageParams: { postId: "123" },
                enableScript: false
            });
        });
    </script>
}
اگر می‌خواهید فونت پیش فرض آن را تنظیم کنید، باید مطابق کدهای ابتدای فایل، ویژگی‌های froala-element را تغییر دهید.
سپس این ادیتور را بر روی المان TextArea قرار گرفته در صفحه، فعال می‌کنیم.
در قسمت مقادیر buttons، تمام حالات ممکن پیش بینی شده‌اند. هر کدام را که نیاز ندارید، حذف کنید.
نحوه‌ی تعریف زبان و راست به چپ بودن این ادیتور را با مقدار دهی پارامترهای language و direction ملاحظه می‌کنید.

پارامترهای autosave، saveURL و saveParams کار تنظیم ارسال خودکار محتوای ادیتور را جهت ذخیره‌ی آن در سرور به عهده دارند. بر اساس مقدار autosaveInterval می‌توان مشخص کرد که هر چند میلی ثانیه یکبار این‌کار باید انجام شود.
        /// <summary>
        /// ذخیره سازی خودکار
        /// </summary>
        [HttpPost]
        [ValidateInput(false)]
        public ActionResult FroalaAutoSave(string body, int? postId) // نام پارامتر بادی را تغییر ندهید
        {
            //todo: save body ...
            return new EmptyResult();
        }
در قسمت سمت سرور هم می‌توان این مقادیر ارسالی را در اکشن متدی که ملاحظه می‌کنید، دریافت کرد.
چون قرار است تگ‌های HTML به سرور ارسال شوند، ویژگی ValidateInput به false تنظیم شده‌است.
saveParams آن، برای مقدار دهی پارامترهای اضافی است که نیاز می‌باشند تا به سرور ارسال شوند. مثلا شماره مطلب جاری نیز به سرور ارسال گردد.
در اینجا نام پارامتری که ارسال می‌گردد، دقیقا مساوی body است. بنابراین آن‌را تغییر ندهید.

پارامترهای imageUploadURL و imageParams برای فعال سازی ذخیره تصاویر آن در سرور کاربرد دارند.
اکشن متد مدیریت کننده‌ی آن به نحو ذیل می‌تواند تعریف شود:
        // todo: مسایل امنیتی آپلود را فراموش نکنید
        /// <summary>
        /// ذخیره سازی تصاویر ارسالی
        /// </summary>
        [HttpPost]
        public ActionResult FroalaUploadImage(HttpPostedFileBase file, int? postId) // نام پارامتر فایل را تغییر ندهید
        {
            var fileName = Path.GetFileName(file.FileName);
            var rootPath = Server.MapPath("~/images/");
            file.SaveAs(Path.Combine(rootPath, fileName));
            return Json(new { link = "images/" + fileName }, JsonRequestBehavior.AllowGet);
        }
در اینجا نام پارامتری که به سرور ارسال می‌گردد، دقیقا معادل file است. بنابراین آن‌را تغییر ندهید.
خروجی آن برای مشخص سازی محل ذخیره سازی تصویر در سرور باید یک خروجی JSON دارای خاصیت و پارامتر link به نحو فوق باشد (این مسیر، یک مسیر نسبی است؛ نسبت به ریشه سایت).
imageParams آن برای مقدار دهی پارامترهای اضافی است که نیاز می‌باشند تا به سرور ارسال شوند. مثلا شماره مطلب جاری نیز به سرور ارسال گردد.


استفاده از Froala WYSIWYG Editor در ASP.NET Web forms
تمام نکاتی که در قسمت تنظیمات ASP.NET MVC در مورد ویژگی‌های سمت کلاینت این ادیتور ذکر شد، در مورد وب فرم‌ها نیز صادق است. فقط قسمت مدیریت سمت سرور آن اندکی تفاوت دارد.
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master"
    ValidateRequest="false"
    EnableEventValidation="false"
    AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="FroalaWebFormsTest.Default" %>

<%--اعتبارسنجی ورودی غیرفعال شده چون باید تگ ارسال شود--%>
<%--همچنین در وب کانفیگ هم تنظیم دیگری نیاز دارد--%>

<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
    <%--حالت کلاینت آی دی بهتر است تنظیم شود در اینجا--%>
    <asp:TextBox ID="txtEditor" ClientIDMode="Static"
        runat="server" Height="199px" TextMode="MultiLine" Width="447px"></asp:TextBox>
    <br />
    <asp:Button ID="btnSave" runat="server" OnClick="btnSave_Click" Text="ارسال" />

    <style type="text/css">
        /*تنظیم فونت پیش فرض ادیتور*/
        .froala-element {
        }
    </style>

    <script type="text/javascript">
        $(function () {
            $('#txtEditor').editable({
                buttons: ["bold", "italic", "underline", "strikeThrough", "fontFamily",
                    "fontSize", "color", "formatBlock", "align", "insertOrderedList",
                    "insertUnorderedList", "outdent", "indent", "selectAll", "createLink",
                    "insertImage", "insertVideo", "undo", "redo", "html", "save", "inserthorizontalrule"],
                inlineMode: false,
                inverseSkin: true,
                preloaderSrc: 'Content/img/preloader.gif',
                allowedImageTypes: ["jpeg", "jpg", "png"],
                height: 300,
                language: "fa",
                direction: "rtl",
                fontList: ["Tahoma, Geneva", "Arial, Helvetica", "Impact, Charcoal"],
                autosave: true,
                autosaveInterval: 2500,
                saveURL: 'FroalaHandler.ashx',
                saveParams: { postId: "123" },
                spellcheck: true,
                plainPaste: true,
                imageButtons: ["removeImage", "replaceImage", "linkImage"],
                borderColor: '#00008b',
                imageUploadURL: 'FroalaHandler.ashx',
                imageParams: { postId: "123" },
                enableScript: false
            });
        });
    </script>
</asp:Content>
همانطور که ملاحظه می‌کنید،  ValidateRequest صفحه به false تنظیم شده و همچنین در وب کانفیگ httpRuntime requestValidationMode به نگارش 2 تنظیم گردیده‌است تا بتوان توسط این ادیتور تگ‌های ارسالی را به سرور ارسال کرد.
به علاوه ClientIDMode=Static نیز تنظیم شده‌است، تا بتوان از ID تکست باکس قرار گرفته در صفحه، به سادگی در کدهای سمت کاربر جی‌کوئری استفاده کرد.
اگر دقت کرده باشید، save urlها اینبار به فایل FroalaHandler.ashx اشاره می‌کنند. محتوای این Genric handler را ذیل مشاهده می‌کنید:
using System.IO;
using System.Web;
using System.Web.Script.Serialization;

namespace FroalaWebFormsTest
{
    public class FroalaHandler : IHttpHandler
    {
        //todo: برای اینکارها بهتر است از وب ای پی آی استفاده شود
        //todo: یا دو هندلر مجزا یکی برای تصاویر و دیگری برای ذخیره سازی متن

        public void ProcessRequest(HttpContext context)
        {
            var body = context.Request.Form["body"];
            var postId = context.Request.Form["postId"];
            if (!string.IsNullOrWhiteSpace(body) && !string.IsNullOrWhiteSpace(postId))
            {
                //todo: save changes

                context.Response.ContentType = "text/plain";
                context.Response.Write("");
                context.Response.End();
            }

            var files = context.Request.Files;
            if (files.Keys.Count > 0)
            {
                foreach (string fileKey in files)
                {
                    var file = context.Request.Files[fileKey];
                    if (file == null || file.ContentLength == 0)
                        continue;

                    //todo: در اینجا مسایل امنیتی آپلود فراموش نشود
                    var fileName = Path.GetFileName(file.FileName);
                    var rootPath = context.Server.MapPath("~/images/");
                    file.SaveAs(Path.Combine(rootPath, fileName));


                    var json = new JavaScriptSerializer().Serialize(new { link = "images/" + fileName });
                    // البته اینجا یک فایل بیشتر ارسال نمی‌شود
                    context.Response.ContentType = "text/plain";
                    context.Response.Write(json);
                    context.Response.End();
                }
            }

            context.Response.ContentType = "text/plain";
            context.Response.Write("");
            context.Response.End();
        }

        public bool IsReusable
        {
            get { return false; }
        }
    }
}
در اینجا نحوه‌ی مدیریت سمت سرور auto save و همچنین ارسال تصاویر ادیتور Froala ، ذکر شده‌اند. با استفاده از context.Request.Form می‌توان به عناصر ارسالی به سرور دسترسی پیدا کرد. همچنین توسط context.Request.Files، اگر فایلی ارسال شده بود، ذخیره شده و نهایتا خروجی JSON مدنظر بازگشت داده می‌شود.


یک نکته‌ی امنیتی مهم
<location path="upload">
  <system.webServer>
    <handlers accessPolicy="Read" />
  </system.webServer>
</location>
تنظیم فوق را در web.config سایت، جهت Read only کردن پوشه‌ی ارسال تصاویر، حتما مدنظر داشته باشید. در اینجا فرض شده‌است که پوشه‌ی uploads قرار است قابلیت اجرای فایل‌های پویا را نداشته باشد.


کدهای کامل این مطلب را در ادامه می‌توانید دریافت کنید
Froala-Sample
 
نظرات مطالب
رمزنگاری JWT و افزایش امنیت آن در ASP.NET Core
این سؤال بیشتر مرتبط هست به مطلب «معرفی JSON Web Token» و تفاوت‌های آن با یک Guid، در داشتن امضای دیجیتال جهت اطمینان حاصل کردن از عدم دستکاری آن توسط کاربر، داشتن تاریخ انقضاء، تا امکان قرار دادن Claims و نقش‌های کاربر در آن جهت استفاده‌ی در برنامه‌های SPA و ... است. 
نظرات مطالب
طراحی افزونه پذیر با ASP.NET MVC 4.x/5.x - قسمت دوم
این کد (در حالت تنظیمات integrated mode) باعث خواهد شد تا تمام درخواست‌های رسیده (منجمله درخواست دریافت فایل‌های استاتیک)، توسط موتور ASP.NET پردازش شود و IIS پیش از آن راسا اقدام نکند.
مطالب
تحلیل و بررسی ده روش آسیب پذیری نرم افزار بر اساس متدولوژی OWASP - قسمت دوم (Cross Site Scripting (XSS - بخش اول: XSS چیست و چگونه کار میکند؟
XSS یکی از شایع‌ترین آسیب پذیری‌های برنامه‌های تحت وب به حساب می‌آید و هنگامی رخ میدهد که برنامه، از ورودی‌های غیر معتبر یا کدگذاری نشده‌ی کاربر، در خروجی تولید شده، استفاده نماید. در این روش مهاجم مستقیماً قربانی را مورد هدف قرار نمیدهد؛ بلکه از نقاط آسیب پذیر در برنامه‌ی تحت وب که توسط قربانی مورد بازدید قرار میگیرد، استفاده میکند. در این روش، وب سایت آسیب پذیر، وسیله‌ی انتقال و رساندن اسکریپتِ مخرب به مرورگر قربانی است.

با XSS  میتوان VBScript,ActiveX و Flash را مورد تهاجم قرار داد؛ اما بیشترین و مهم‌ترین هدف این حملات، JavaScript است. برای اجرای کد جاوااسکریپتِ مخرب در مرورگر، مهاجم باید کد خود را در بین صفحاتی که قربانیان از آن بازدید میکنند، جا دهد (منظور از قربانیان، کاربرانی هستند که از صفحاتِ آلوده به کد مخرب بازدید میکنند).

JavaScript دسترسی محدودی به سیستم عامل و فایلها دارد (به‌دلایل امنیتی): 
  • جاوا اسکریپت توانایی خواندن، نوشتن، کپی و اجرای فایلها را بر روی هارد دیسک ندارد و همچنین دسترسی مستقیمی به سیستم عامل ندارد.
  • مرورگرهای مدرن اجازه کار با فایلها را به جاوا اسکریپت میدهند؛ بشرطی که آن فایل از طریق input درون صفحه وب و با اجازه کاربر انتخاب شود و فقط محدود به دسترسی به همان فایل است.


با توجه با محدودیتهای بالا چگونه اسکریپت‌های مخرب امنیت ما را به خطر می‌اندازند؟

  • جاوااسکریپت به کوکی‌ها دسترسی دارد (کوکی، فایل‌های متنی کوچکی برای ذخیره اطلاعات کاربر از یک سایت است؛ مانند نام کاربری و کلمه عبور). از کوکی‌ها برای ذخیره‌ی توکن نشست‌ها استفاده می‌شود. لذا اگه هکر بتواند کوکی نشست یک کاربر را بدست بیاورد، میتواند از هویت او استفاده کند و خود را بجای کاربر مورد نظر قرار دهد.
  • جاوااسکریپت امکان تغییرات در DOM مرورگر را دارد (داخل صفحه‌ای که جاوااسکریپت در آن اجرا شده است).
  • جاوا اسکریپت با استفاده از شیء XMLHttpRequest میتواند درخواست‌های HTTP را با محتوای دلخواه، به مقصد مورد نظر ارسال نماید.
  • در مرورگرهای مدرن امروزی امکان دسترسی به دوربین، میکروفن، GPS وفایلهای خاصی از طریق APIهای HTML5 امکانپذیر هست.
     

انواع روشهای XSS 

  • روش بازتابی Reflected XSS Attack  
  • روش ذخیره شده Stored XSS Attack 
  • روش مبتنی بر  DOM-based XSS 

 
روش بازتابی  Reflected XSS Attack
در این روش، اسکریپتِ مخرب، بخشی از درخواستِ قربانی از وب سایت است. وب سایت هم آن را بعنوان پاسخ به کاربر ارسال میکند. 
a) هکر، یک URL شامل رشته‌ی مخرب را میسازد و آن را برای قربانی ارسال میکند.
b) قربانی توسط هکر به روشهای مختلفی از جمله مهندسی اجتماعی فریب میخورد تا آن URL را درخواست کند.
c) وب سایت، رشته‌ی مخربِ درونِ URL را در پاسخ به کاربر قرار میدهد.
d) مرورگر قربانی، اسکریپتِ مخربِ موجود در پاسخ را اجرا میکند و در نتیجه کوکی قربانی به جایی که هکر مشخص کرده ارسال میشود.


شاید برای شما سوال باشد که چطور قربانی، یک URL شاملِ کدمخربی را ارسال میکند که خودش را هک کند! در صورتیکه کسی، عمدا خودش را هک نمیکند. اما دو راه برای چنین موردی وجود دارد:
a) هکر میتواند یک کاربر خاص را هدف قرار دهد و URL مخرب را از طریق ایمیل و یا سایر پیام رسانها برای او ارسال کند و با استفاده از مهندسی اجتماعی او را ترغیب به درخواست آن URL کند.
b) اگر هکر قصد داشته باشد تعداد کاربران زیادی را  مورد هدف قرار دهد، میتواند لینکی (URL مخرب) را در وب سایت خودش یا شبکه‌های اجتماعی منتشر کند و منتظر باشد تا کاربران آن را درخواست کنند (روی آن کلیک کنند).


روش ذخیره شده Stored XSS Attack
در این روش، نفوذگر کدِمخرب را در بانک اطلاعاتی وارد (inject) می‌کند. فرض کنید در قسمت کامنت محصولات، باگ XSS وجود دارد و هکر میتواند اسکریپتِ مخربی را وارد کند. این کدِمخرب، درون پایگاه داده ذخیره میشود و هر کاربری که از این صفحه بازدید کند، کد مخرب بر روی مرورگرش اجرا میشود.



روش مبتنی بر  DOM-based XSS 
a) هکر یک URL شامل کدمخرب را برای قربانی ارسال میکند.
b) قربانی روی لینک کلیک میکند. وب سایت، درخواست را پاسخ میدهد ولی کدمخربی درون پاسخ قرار نمیگیرد.
c) مرورگرِ قربانی با اجرایِ اسکریپت غیرمخرب درون پاسخ، باعث وارد شدن اسکریپتِ مخرب، درون صفحه میشود.
d) با اجرای کدِمخرب درون مرورگر قربانی، کوکی او برای مقصدی که هکر مشخص کرده ارسال میشود.
نظرات مطالب
اعتبارسنجی مبتنی بر JWT در ASP.NET Core 2.0 بدون استفاده از سیستم Identity
فرض کنید در سیستمی از refresh token استفاده نشده یعنی در واقع توکن در دیتابیس ذخیره نمی‌شود فقط در سمت سرور توکن به صورت in-memory تولید شده و کاربران تا زمان انقضای آن قادر به استفاده از API هستند این زمان نیز به 15 روز ست شده است. حالا بعد از اعمال روش ارائه شده در این مطلب (ذخیره‌سازی token و refresh token در دیتابیس) چطور می‌توانیم کاربرانی که از توکن قبلی استفاده می‌کنند را مجبور به Sign out کنیم؟ کلاینت‌ها هم اندروید و آی‌اوس هستند و چیزی حدود 100 کاربر در حال استفاده از اپلیکیشن است. روشی که به ذهنم رسید خالی کردن کش اپلیکیشن درون گوشی هست اما اینکار برای 100 یوزر منطقی نیست. آیا روشی وجود دارد که توکن‌ها اکتیو را از سمت سرور منقضی کنیم؟ 
لاگ سمت سرور نیز این exception رو نمایش میدهد:
Microsoft.IdentityModel.Tokens.SecurityTokenExpiredException: IDX10223: Lifetime validation failed. The token is expired. ValidTo: '[PII is hidden by default. Set the 'ShowPII' flag in IdentityModelEventSource.cs to true to reveal it.]', Current time: '[PII is hidden by default. Set the 'ShowPII' flag in IdentityModelEventSource.cs to true to reveal it.]'.
at Microsoft.IdentityModel.Tokens.Validators.ValidateLifetime(Nullable`1 notBefore, Nullable`1 expires, SecurityToken securityToken, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateLifetime(Nullable`1 notBefore, Nullable`1 expires, JwtSecurityToken jwtToken, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateTokenPayload(JwtSecurityToken jwtToken, TokenValidationParameters validationParameters)
at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.<HandleAuthenticateAsync>d__6.MoveNext()
روش فوق به خوبی برای کاربران جدید کار می‌کنه اما مشکل اصلی کاربرانی هستند که در حال استفاده از توکن قبلی هستند.
موردی که به ذهنم میرسه سفارشی‌سازی AuthorizeAttribute هستش (یعنی بررسی توکن اکتیو با نمونه‌ی موجود در دیتابیس اگر یکی نبود HandleUnauthorizedRequest رو به ریسپانس برمی‌گردونم) این مورد رو هم تست می‌کنم و نتیجه رو همینجا اعلام می‌کنم.
مطالب
آشنایی با WPF قسمت پنجم : DataContext بخش دوم
در ادامه قسمت قبلی قصد داریم دو کنترل دیگر را نیز بایند کنیم؛ ولی از آنجا که مقادیر آن‌ها رشته‌ای یا عددی نیست و مقداری متفاوت هست، از مبحثی به نام ValueConverter استفاده خواهیم کرد.
Value Converter چیست؟


موقعی که شما قصد بایند کردن دو نوع داده متفاوت را به هم دارید، نیاز به یک کد واسط پیدا می‌کنید تا این کد واسط مقادیر شما را از مبدا دریافت کرده و تبدیل به نوعی کند که مقصد بتواند از آن استفاده کند یا بلعکس. ValueConverter نام کلاسی است که از یک اینترفیس به نام IValueConverter ارث بری کرده است و شامل دو متد تبدیل نوع از مبدا به مقصد Convert و دیگری از مقصد به مبدا  ConvertBack می‌شود که خیلی کمتر پیاده سازی می‌شود.
پیاده سازی یک کلاس مبدل سه مرحله دارد:
  • مرحله اول :ساخت کلاس ValueConverter
  • مرحله دوم : تعریف آن به عنوان یک منبع یا ریسورس
  • مرحله سوم : استفاده از آن در عملیات بایند کردن Binding

در مرحله اول، نحوه پیاده سازی کلاس ValueConverter به شکل زیر است:
public class BoolToVisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, 
        object parameter, CultureInfo culture)
    {
        // Do the conversion from bool to visibility
    }
 
    public object ConvertBack(object value, Type targetType, 
        object parameter, CultureInfo culture)
    {
        // Do the conversion from visibility to bool
    }
}

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

در مرحله‌ی دوم نحوه تعریف مبدل ما در پنجره XAML به صورت زیر می‌باشد:
<Window x:Class="test.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:نامی دلخواه="clr-namespace:فضای نامی که کلاس مبدل در آن قرار دارد"
        >

    <Window.Resources>
        <نام دلخواهی که در بالا تعریف کرده اید: ClassNameنام کلاس  x:Key="کلید این آیتم در ریسورس"/>
    </Window.Resources>

کلمه‌های کلیدی xmlns که مخفف XML NameSpace هستند جهت تعریف دسترسی به فضاهای نام طراحی شده‌اند و طبق تعریف مایکروسافت همان مفهوم تگ‌های تعریف فضای نام در XML را دارند که توسط مایکروسافت توسعه یافته‌اند. این تعریف‌ها در تگ ریشه (در اینجا window) تعریف می‌شوند. دو فضای نام اولی که به طور پیش فرض در همه جای پروژه قرار دارند، اشاره به فریم ورک WPF دارند. کلمه‌ی کلیدی x در خط شماره سه، نام دلخواهی است که دسترسی ما را به خصوصیات یا تعاریف XAML موجود در sdk باز می‌کند؛ مثلا استفاده از خصوصیاتی چون x:key یا x:class را به همراه دارد.
پس الان باید خط چهارم برای ما روشن باشد؛ فضای نام جدیدی را در برنامه خودمان ایجاد کرده‌ایم که این تگ به آن اشاره می‌کند و نام دلخواهی هم برای اشاره به این فضای نام برایش در نظر گرفته‌ایم. هر موقع در برنامه این نام دلخواه تعیین شده قرار گیرد، یعنی اشاره به این فضای نام که در قسمت Window.resource خط هشتم تعریف شده است.
در خط هشتم، یک ریسورس (منبع) را به برنامه معرفی کرده‌ایم:
ریسورس‌ها برای ذخیره سازی داده‌ها در سطح یک کنترل، سطح محلی در یک پنجره، یا سطح عمومی در کل پروژه به کار می‌روند. محدودیتی در ذخیره داده‌ها وجود ندارد و هر چقدر که دوست دارید می‌توانید داده به آن پاس کنید. این داده‌ها می‌توانند یک سری اطلاعات ذخیره شده در یک ساختار ساده تا یک ساختار سلسه مراتبی از کنترل‌ها باشند. ریسورس‌ها به شما این اجازه را می‌دهند تا داده‌ها را در یک مکان ذخیره کرده و آن‌ها را در یک یا چندجا مورد استفاده قرار دهید.

از آن جا که مباحث ریسورس‌ها را در یک مقاله‌ی جداگانه بررسی می‌کنیم، فقط به ذکر نکات بالا جهت کد فعلی بسنده خواهیم کرد و ادامه‌ی آن را در یک مقاله دیگر مورد بررسی قرار می‌دهیم.
هر ریسورس دارای یک نام یا یک کلید است که با خصوصیت x:key تعریف می‌شود.
ریسورس بالا یک کلاس را که در فضای نام دلخواهی قرار دارد، تعریف می‌کند و یک کلید هم به آن انتساب می‌دهد.
مرحله‌ی سوم معرفی ریسورس به عملیات Binding است:
{Binding نام پراپرتی کلاس, Converter={StaticResource کلید آیتم مربوطه در ریسورس}, ConverterParameter=پارامتری که به کلاس مبدل پاس می‌شود}

بخش اول دقیقا همان چیزی است که در قسمت قبلی یاد گرفتیم. معرفی پراپرتی که باید عمل بایند به آن صورت گیرد. قسمت بعدی معرفی مبدل است و از آن جا که تابع مبدل ما در یک منبع است، اینگونه می‌نویسیم: {} را باز کرده و ابتدا کلمه StaticResource فاصله و سپس کلید ریسورس که تابع را از ریسورس فراخوانی کند و قسمت بعدی هم پاس کردن یک پارامتر به تابع مبدل است.

حال که با اصول نوشتار آشنا شدیم کار را آغاز می‌کنیم.
قصد داریم یک مبدل برای فیلد جنیست درست کنیم. از آنجا که این فیلد Boolean است و خصوصیت IsChecked یک RadioButton هم Boolean است، می‌توان یک ارتباط مستقیم را ایجاد کرد. ولی مشکل در اینجا هست که True برای مذکر است و false برای مؤنث. در نتیجه تنها Radiobutton مربوطه به جنس مذکر به این حالت پاسخ می‌دهد و از آنجا که  برای جنس مونث  false در نظر گرفته شده است، انتخاب آن هم false خواهد بود. پس باید در مبدل، مقداری که کنترل می‌خواهد را شناسایی کرده و اگر مقدار با آن برابر بود، True را بازگردانیم. مقداری که هر کنترل درخواست می‌کند را از طریق پارامتر به تابع مبدل ارسال می‌کنیم. radiobutton مذکر، مقدار True را به عنوان پارامتر ارسال می‌کند و radiobutton مونث هم مقدار false را به عنوان پارامتر ارسال می‌کند. اگر تابع مبدل را ببینید، این مقدار‌ها با پارامترها همخوانی دارند، True در غیر این صورت false بر میگرداند.

مرحله اول تعریف کلاس ValueConveter:
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

namespace test.ValueConverters
{
    public class GenderConverter: IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var ParameterString = parameter as string;
            if (ParameterString == null)
                return DependencyProperty.UnsetValue;

            bool bparam;

            bool test = bool.TryParse(parameter.ToString(), out bparam);
            if (test)
            {
                return ((bool)value).Equals(bparam);
            }
            return DependencyProperty.UnsetValue;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}
در کد بالا پارامتر ارسالی را دریافت می‌کنیم و اگر برابر با Null باشد، مقداری برگشتی را عدم ذکر شدن خصوصیت وابسته اعلام می‌کنیم. در نتیجه انگار این خصوصیت مقداردهی نشده است. اگر مخالف Null باشد، کار ادامه پیدا می‌کند. در نهایت مقایسه‌ای بین پارامتر و مقدار پراپرتی (value) صورت گرفته و نتیجه‌ی مقایسه را برگشت می‌دهیم.

برای تعریف این مبدل در محیط XAML به صورت زیر اقدام می‌کنیم:
<Window x:Class="test.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:valueConverters="clr-namespace:test.ValueConverters"
        Title="MainWindow" Height="511.851" Width="525">

    <Window.Resources>
        <valueConverters:GenderConverter x:Key="GenderConverter"/>
    </Window.Resources>
</window>
 کلمه valueConveter به فضای نام test.valueConverters اشاره می‌کند که در قسمت ریسورس، از کلاس GenderConverter آن استفاده می‌کنیم و کلیدی که برای این ریسورس در نظر گرفتیم را GenderConverter تعریف کردیم (هم نامی کلید ریسورس و نام کلاس مبدل اتفاقی است و ارتباطی با یکدیگر ندارند).

نکته: در صورتی که بعد از تعریف ریسورس با خطای زیر روبرو شدید و محیط طراحی Design را از دست دادید یکبار پروژه را بیلد کنید تا مشکل حل شود.
The name "GenderConverter" does not exist in the namespace "clr-namespace:test.ValueConverters".


اکنون در عملیات بایندینگ دو کنترل اینگونه می‌نویسیم:
     <RadioButton GroupName="Gender" IsChecked="{Binding Gender, Converter={StaticResource GenderConverter}, ConverterParameter=True}" Name="RdoMale"  >Male</RadioButton>
                <RadioButton GroupName="Gender" IsChecked="{Binding Gender, Converter={StaticResource GenderConverter}, ConverterParameter=False}" Name="RdoFemale" Margin="0 5 0 0"  >Female</RadioButton>
حال برنامه را اجرا کرده و نتیجه را ببینید. برای تست بهتر می‌توانید جنسیت فرد را در منبع داده تغییر دهید. همچنین از آنجا که این مقدار برای جنس مذکر نیازی به بررسی و تبدیل ندارد می‌توانید عملیات بایند را عادی بنویسید:
<RadioButton GroupName="Gender" IsChecked="{Binding Gender}" Name="RdoMale"  >Male</RadioButton>
این کد می‌تواند برای تمامی وضعیت‌های دو مقداری چون جنسیت و وضعیت تاهل و ... هم به کار برود. ولی حالا سناریویی را تصور کنید که که مقادیر از دو تا بیشتر شود؛ مثل وضعیت تحصیلی ، دسترسی‌ها و غیره که این‌ها نیاز به داده‌های شمارشی چون Enum‌ها دارند. روند کار دقیقا مانند بالاست:
هر کنترل مقداری از یک enum را میپذیرد که میتواند آن مقدار را با استفاده از پارامتر، به تابع مبدل ارسال کند و سپس تابع چک می‌کند که آیا چنین مقداری در enum مدنظر یافت می‌شود یا خیر؛ این کد الان کار می‌کندو واقعا هم کد درستی است و هیچ مشکلی هم ندارد. ولی برای یک لحظه تصور کنید پنجره شما شامل چهار خصوصیت enum دار است. یعنی الان باید 4 تابع مبدل بنویسید. پس باید کد را بازنویسی کنیم تا به هر enum که مدنظر است پاسخ دهد؛ به این ترتیب تنها یک تابع مبدل می‌نویسیم.

جهت یادآوری نگاهی به کلاس برنامه می‌اندازیم:
public enum FieldOfWork
    {
        Actor=0,
        Director=1,
        Producer=2
    }
    public class Person : INotifyPropertyChanged
    { 
        public bool Gender { get; set; }

        public string ImageName { get; set; }

        public string Country { get; set; }

        public DateTime Date { get; set; }

        public FieldOfWork FieldOfWork { get; set; }

        private string _name;
        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                OnPropertyChanged();
            }
        }
فعلا IList را از پراپرتی FieldOfWork بر میداریم تا این سناریو باشد که تنها یک Enum قابل انتخاب است:
public FieldOfWork FieldOfWork { get; set; }
بعدا حالت قبلی را بررسی میکنیم.

کد کلاس مبدل را به صورت زیر می‌نویسیم:
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;

namespace test.ValueConverters
{
    public class EnumConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var ParameterString = parameter as string;
            if (ParameterString == null)
                return DependencyProperty.UnsetValue;

            if (Enum.IsDefined(value.GetType(), value) == false)
                return DependencyProperty.UnsetValue;

            object paramvalue = Enum.Parse(value.GetType(), ParameterString);
            return paramvalue.Equals(value);
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}
در مرحله اول مثل کد قبلی بررسی می‌شود که آیا پارامتری ارسال شده است یا خیر. در مرحله دوم بررسی می‌شود که نوع داده مقدار پراپرتی چیست یعنی چه Enum ایی مورد استفاده قرار گرفته است. اگر در Enum مقداری که در پارامتر به آن ذکر شده است وجود نداشته باشد، بهتر هست که کار در همین جا به پایان برسد؛ زیرا که یک پارامتر اشتباهی ارسال شده است و چنین مقداری در Enum وجود ندارد. در غیر اینصورت کار ادامه می‌یابد و پارامتر را به enum تبدیل کرده و با مقدار مقایسه می‌کنیم. اگر برابر باشند نتیجه true را باز میگردانیم.

کد قسمت ریسورس را با کلاس جدید به روز می‌کنیم:
    <Window.Resources>
        <valueConverters:GenderConverter x:Key="GenderConverter"/>
        <valueConverters:EnumConverter x:Key="EnumConverter"></valueConverters:EnumConverter>
    </Window.Resources>
کد چک باکس‌ها هم به شکل زیر تغییر می‌یابد:
    <CheckBox Name="ChkActor" IsChecked="{Binding FieldOfWork, Converter={StaticResource EnumConverter}, ConverterParameter=Actor}" >Actor/Actress</CheckBox>
                <CheckBox Name="ChkDirector" IsChecked="{Binding FieldOfWork, Converter={StaticResource EnumConverter}, ConverterParameter=Director}" >Director</CheckBox>
                <CheckBox Name="ChkProducer" IsChecked="{Binding FieldOfWork, Converter={StaticResource EnumConverter}, ConverterParameter=Producer}" >Producer</CheckBox>
کلاس GetPerson که منبع داده ما را فراهم می‌کند هم به شکل زیر است:
  public static Person GetPerson()
        {
            return new Person()
            {
                Name = "Leo",
                Gender =true,
                ImageName ="man.jpg",
                Country = "Italy",
                FieldOfWork = test.FieldOfWork.Actor,
                Date = DateTime.Now.AddDays(-3)
            };
        }

برنامه را اجرا کنید تا نتیجه کار را ببینید. باید چک باکس Actor تیک خورده باشد. میتوانید منبع داده را تغییر داده تا نتیجه کار را ببینید.

بگذارید فیلد FieldOfWork را به حالت قبلی یعنی IList برگردانیم. در بسیاری از اوقات ما چند گزینه از یک Enum را انتخاب می‌کنیم، مثل داشتن چند سطح دسترسی یا چند سمت کاری و ...

کلاس را به همراه کد GetPerson به شکل زیر تغییر می‌دهیم:
    public enum FieldOfWork
    {
        Actor=0,
        Director=1,
        Producer=2
    }
    public class Person : INotifyPropertyChanged
    { 
        public bool Gender { get; set; }

        public string ImageName { get; set; }

        public string Country { get; set; }

        public DateTime Date { get; set; }

        public IList<FieldOfWork> FieldOfWork { get; set; }

        private string _name;
        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                OnPropertyChanged();
            }
        }

        public static Person GetPerson()
        {
            return new Person
            {
                Name = "Leo",
                Gender = true,
                ImageName = "man.jpg",
                Country = "Italy",
                FieldOfWork = new FieldOfWork[] { test.FieldOfWork.Actor, test.FieldOfWork.Producer },
                Date = DateTime.Now.AddDays(-3)
            };
        }
}

دو Enum بازیگر و تهیه کننده را انتخاب کرده‌ایم، پس در زمان اجرا باید این دو گزینه انتخاب شوند.
کد مبدل را به صورت زیر می‌نویسیم:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;

namespace test.ValueConverters
{
    public class EnumList : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var ParameterString = parameter as string;
            if (ParameterString == null)
                return DependencyProperty.UnsetValue;

         var enumlist= (IList) value;         

            if(enumlist==null && enumlist.Count<1)
                return DependencyProperty.UnsetValue;

            if (Enum.IsDefined(enumlist[0].GetType(), ParameterString) == false)
                return DependencyProperty.UnsetValue;


            /*
              foreach (var item  in enumlist)
            {
                object paramvalue = Enum.Parse(item.GetType(), ParameterString);
                bool result = item.Equals(paramvalue);
                if (result)
                    return true;
            }
            return false;
             */
            return (from object item in enumlist let paramvalue = Enum.Parse(item.GetType(), ParameterString) select item.Equals(paramvalue)).Any(result => result);
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}
مقدار پراپرتی را به نوع IList تبدیل می‌کنید و اگر لیست معتبر بود، برنامه را ادامه می‌دهیم؛ در غیر اینصورت تابع خاتمه می‌یابد. بعد از اینکه صحت وجود لیست اعلام شد، بررسی میکنیم آیا Enum چنین مقداری که پارامتر ذکر کرده است را دارد یا یک پارامتر اشتباهی است.
در حلقه‌ای که به شکل توضیح درآمده، همه آیتم‌های مربوطه در لیست را بررسی کرده و اگر آیتمی برابر پارامتر باشد، True بر میگرداند و در صورتی که حلقه به اتمام برسد و آیتم پیدا نشود، مقدار False را برمی‌گرداند. این حلقه از آن جهت به شکل توضیح درآمده است که کد Linq آن در زیر نوشته شده است.

تعریف کلاس بالا در ریسورس:
   <Window.Resources>
        <valueConverters:GenderConverter x:Key="GenderConverter"/>
        <valueConverters:EnumConverter x:Key="EnumConverter"></valueConverters:EnumConverter>
        <valueConverters:EnumList x:Key="EnumList"></valueConverters:EnumList>
    </Window.Resources>
و تغییر کلید ریسورس در خطوط چک باکس ها، باقی موارد همانند قبل ثابت هستند:
     <CheckBox Name="ChkActor" IsChecked="{Binding FieldOfWork, Converter={StaticResource EnumList}, ConverterParameter=Actor}" >Actor/Actress</CheckBox>
                <CheckBox Name="ChkDirector" IsChecked="{Binding FieldOfWork, Converter={StaticResource EnumList}, ConverterParameter=Director}" >Director</CheckBox>
                <CheckBox Name="ChkProducer" IsChecked="{Binding FieldOfWork, Converter={StaticResource EnumList}, ConverterParameter=Producer}" >Producer</CheckBox>

نتیجه‌ی کار باید به شکل زیر باشد:
فیلد جنسیت و زمینه کاری از تابع مبدل به دست آمده است.

  بدیهی است خروجی‌های بالا برای کنترل هایی است که مقدار Boolean را می‌پذیرند و برای سایر کنترل‌ها باید با کمی تغییر در کد و نوع برگشتی که تحویل خروجی متد مبدل می‌شود، دهید.

دانلود فایل‌های این قسمت
بازخوردهای دوره
ارتباطات بلادرنگ و SignalR
- کار ما در اینجا پشتیبانی مقالات سایت‌های دیگر نیست. ضمنا در قسمت اول، یک مبحث کاملا خارج از موضوع را نباید ارسال کنید. در ذیل هر مطلب عنوان شده برای این نوع موارد مرتبط به دوره، لطفا به قسمت پرسش و پاسخ دوره مراجعه کنید.
- من از جزئیات کار شما اطلاعی ندارم. نه خطایی را عنوان کردید و نه پروژه‌ای برای دیباگ پیوست شده. ایشان عنوان کرده که اجرا می‌شود؛ یک فیلم هم پیوست کرده. ضمنا با jQuery Ajax کار کرده قسمتی را. یعنی یک سری پیشنیاز دیگر را هم باید به پروژه و صفحه اضافه کنید. در کل از راه دور و بدون دیدن کار شما نمی‌شود نظر داد (کل کار البته).
- در آن مقاله سایت ثالث، dependency_OnChange فقط زمانی رجیستر می‌شود که GetData یکبار فراخوانی شود. ضمنا این کد نشتی حافظه دارد. چون مدام دارد new OnChangeEventHandler را ایجاد می‌کند بدون اینکه فکری برای حذف موارد ثبت شده کند. همچنین JobInfoRepository را در سطح یک Web API Controller وهله سازی کرده. یعنی این وهله به ازای هر درخواست رسیده یکبار ایجاد می‌شود (ونه اینکه یکبار ایجاد شده و بارها استفاده شود) و به این ترتیب یکبار دیگر نیز OnChangeEventHandler رجیستر خواهد شد. خلاصه اینکه روش مناسبی نبوده و توصیه نمی‌شود.

مطلبت حذف شد. تکرار کنی خودت هم حذف می‌شی. شک نداشته باش.