امنیت و اعتبار سنجی درNET.

در این مقاله به اعتبارسنجی در دات نت ، اهمیت آن و روش پیاده سازی آن خواهیم پرداخت . اعتبارسنجی که در این مقاله و پروژه ضمیمه بحث خواهد شد، اعتبارسنجی سمت سرور می باشد

امنیت و اعتبار سنجی درNET.

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

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

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

امینت سمت سرور

امنیت سمت سرور زمانی اهمیت ویژه می یابد که ما فرمی روی صفحه داریم و قصد داریم اطلاعاتی که کاربر وارد می کند را ذخیره کنیم

در سمت سرور باید کنترل کرد که اطلاعاتی که کاربر وارد کرده استValid  می باشد یا نه ، در صورت درست بودن این اطلاعات ذخیره خواهند شد و در صورت خطا داشتن صفحه Validation به کاربر نمایش داده می شود تا اطلاعات را تصحیح کرده و دوباره به سمت سرور ارسال کند.

در صفحات قدیمی aspx/ascx در ویژال استودیو، ابزارهای Validation وجود داشت مثل Asp:RequiredFieldValidator که باعث می شدند اطلاعات  وارد شده توسط کاربر ارزیابی شود و داده ها با قالب (فرمت ) صحیح برای ذخیره در بانک طلاعاتی ارسال شوند . این کار در MVC با استفاده از Model  Validation انجام می شود . در واقع با این کار، کنترل می کنیم که کاربران وقتی اطلاعاتی در فرم وارد می کنند، این اطلاعات از لحاظ قالب داده، صحیح باشند، مثلا ایمیل با فرمت صحیح وارد شود، جایی که قرار است عدد وارد ، حروف وارد نشود و .... 

در  پروژه ای که به صورت ضمیمه همراه مقاله وجود دارد ما از اعتبار سنجی سمت سرور استفاده کرده ایم .در ویژوال 2013 یک پروژه MVC  ایجاد کنید.

 public class UserModel
    {
        [Key]
        public int Id { get; set; }
        [Required(ErrorMessage = "لطفا نام خود را وارد کنید", AllowEmptyStrings = false)]
        [Display(Name = "نام")]
        [DisplayName("نام")]
        [StringLength(50, ErrorMessage = "طول این فیلد نباید بیشتر از 50 کاراکتر باشد")]
        public string FirstName { get; set; }

        [Required(ErrorMessage = "لطفا نام خود را وارد کنید", AllowEmptyStrings = false)]
        [Display(Name = "نام خانوادگی")]
        [DisplayName("نام خانوادگی")]
        [StringLength(50, ErrorMessage = "طول این فیلد نباید بیشتر از 50 کاراکتر باشد")]
        public string Lastname { get; set; }

        [Required(ErrorMessage = "لطفا آدرس را وارد کنید", AllowEmptyStrings = false)]
        [Display(Name = "آدرس")]
        public string Address { get; set; }
        [DisplayName("شماره موبایل")]
        [Display(Name = "شماره موبایل")]
        [RegularExpression(@"^0?9[123]\d{8}$", ErrorMessage = "شماره موبایل را بدرستی وارد کنید")]
        [StringLength(50, ErrorMessage = "این فیلد باید حداکثر 50 کاراکتر باشد")]
        public string TellNo { get; set; }
    }

از آنجاییکه ما codefirst  کار می کنیم باید یک کلاس Context  که نقش دیتابیس را برای ما بازی خواهد کرد داشته باشیم.نیازی نیست خودتان این کلاس را بسازید بلکه با کلیک راست روی کنترلر و زدن گزینه MVC controller With View Using Entity Framework پنجره ای مطابق شکل زیر برای شما باز خواهد شد:

سپس با زدن دکمه + از mvc  می خواهید که خودش برای شما Context  را ایجاد کند .

فرض کنید قصد داریم صفحه ای به کاربر نشان دهیم که اطلاعات مختلفی را باید در آن وارد کند.

صفحه ویو دارای ساختاری شبیه زیر خواهد بود:

@model WebApplication1.Models.UserModel

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>ثبت نام کاربر</title>
</head>
<body dir="rtl">
    <script src="~/Scripts/jquery-1.10.2.min.js"></script>
    <script src="~/Scripts/jquery.validate.min.js"></script>
    <script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
    
    
    @using (Html.BeginForm()) 
    {
        @Html.AntiForgeryToken()
        
        <div class="form-horizontal">
            <h4>مشخصات کاربر</h4>
            <hr />
            @Html.ValidationSummary(true, "", new { @class = "text-danger" })
            <div class="form-group">
                @Html.LabelFor(model => model.FirstName, htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.EditorFor(model => model.FirstName, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.ValidationMessageFor(model => model.FirstName, "", new { @class = "text-danger" })
                </div>
            </div>
    
            <div class="form-group">
                @Html.LabelFor(model => model.Lastname, htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.EditorFor(model => model.Lastname, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.ValidationMessageFor(model => model.Lastname, "", new { @class = "text-danger" })
                </div>
            </div>
    
            <div class="form-group">
                @Html.LabelFor(model => model.Address, htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.EditorFor(model => model.Address, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.ValidationMessageFor(model => model.Address, "", new { @class = "text-danger" })
                </div>
            </div>
    
            <div class="form-group">
                @Html.LabelFor(model => model.TellNo, htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.EditorFor(model => model.TellNo, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.ValidationMessageFor(model => model.TellNo, "", new { @class = "text-danger" })
                </div>
            </div>
    
            <div class="form-group">
                <div class="col-md-offset-2 col-md-10">
                    <input type="submit" value="Create" class="btn btn-default" />
                </div>
            </div>
        </div>
    }
    
    <div>
        @Html.ActionLink("بازگشت به صفحه اصلی", "Index")
    </div>
</body>
</html>

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

Required : این ویژگی از اعتبارسنجی کاربر را ملزم به وارد کردن دیتا در پراپرتی مربوطه می کندو تا این دیتا پر نشود اطلاعات به سمت سرور Post نخواهد شد.همانطور که می بیند پیغام خطایی هم برای آن نوشته ایم.

Display(name=) : این ویژگی نامی را به پراپرتی شما اختصاص میدهد

RegularExpression : این ویژگی زمانی که قصد داریم اطلاعات در فرمت خاصی مثلا ایمیل موبایل شماره ملی و یا..... باشد مورد استفاده قرار می گیرد.

جلوگیری از حمله های Cross Site forgery

این روش نوعی نفوذ است که در آن هکر از اعتماد یک وب سایت به یکی از کاربران استفاده می‌نماید.به عبارت دیگر سایت به یک کاربر به دلیل اینکه با نام کاربری و کلمه عبور معتبر وارد شده و با موفقیت مورد شناسایی قرار گرفته است اعتماد می‌کند و دادهایی که وی ارسال میکند را به راحتی قبول می‌نماید.

هکر تلاش میکند تا کاربرانی را که به سایت وارد شده و شناسایی شده‌اند را ترغیب نماید تا بر روی لینکی که داده های مورد نظر هکر را ارسال می‌کند کلیک کنند بدون اینکه کاربر متوجه این امر شود.

راه حل مقابله با این بر این فرض بنا شده است که هکر یک فرم دیگر که بر روی سایت اصلی قرار ندارد ساخته و از آن استفاده می‌کند.با استفاده از این موضوع ما می‌توانیم با هکر مقابله کنیم.راه حل ساده آن است که ما از (Attribute)  با عنوان ValidateAnitForgery بر روی متد در حالت POST استفاده کنیم.

رای ساخت نشانه AntiForgery یعنی (AntiForgeryToken)  و Cookie مربوطه بر روی سیستم کاربر ، کدهای زیر را در ویو تعریف می‌کنیم :

@using(Html.Form("UserRegistration", "Register")) {   
   @Html.AntiForgeryToken()   
   //Then here normal rest form as mentioned above   
}  

برای جلوگیری از این نوع حمله ها MVC  از  [ValidatAntiForgeryToken]  استفاده می کند.

کد ان به شکل زیر خواهد بود.

[HttpPost]  
[ValidateAntiForgeryToken]  
public ActionResult UserRegistration(RegistrationViewModel registerModel){  
   //ModeState Valid check and rest code goes here  
}  

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

حمله SQL Injection و جلوگیری از آن

در این نوع حمله دیتاهای مخربی که هکر وارد کند به صورت دستورات SQL  بر روی پایگاه داده ما اجرا خواهند شد.(فاجعه).تصور کنید که هکر قادر خواهد بود که کل دیتا بیس شما را پاک کند !!!!

برای اینکه بدانیم این حمله چگونه کار می کند ، تصور کنید یک کوئری برای به دست آوردن آدرس داریم به شکل زیر:

var sqlTobeExecuted = "SELECT HouseID, HouseName"+  
"FROM House " +  
"WHERE Address LIKE '" + searchInputParam +"%';  
SqlDataAdapter da = new SqlDataAdapter(sqlTobeExecuted , DbCommand);  

این کوئری پارامتریک نیست و درواقع یک رشته است . حال هکر در Textbox  مربوط به سرچ صفحه خط زیر را وارد می کند.

' UNION SELECT id,name FROM sysobjects;--

فرم کلی کوئری که به سمت سرور برای اجرا داده می شود مشابه زیر خواهد بود

var commandToBeExecuted = "SELECT HouseID, HouseName FROM House"+  
"WHERE Address Like @Address";  
SqlCommand cmd = new SqlCommand(commandToBeExecuted , conn);  
cmd.Parameters.Add("@Address",address);  

به همین راحتی دیتا بیس شما حک شد.

راه حل چیست؟

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

2-کوئری های پارامتریک:به جای کوئری های رشته ای از کوئری های پارامتریک استفاده کنید.برای مثال بالا کد ما به شکل زیر درخواهد آمد.

[Authorize]  
public class HomeController : Controller  
{  
   public ActionResult Index()  
   {  
      return View();  
   }  
}  

3-Stored procedure  های پارامتریک:از پروسیجرهای پارامتریک استفاده کنید.

4-استفاده از Entity Framework & LINQ

احراز هویت و دسترسی Authentication & Authorization

بعد از انتشار یک اپلیکیشن احراز هویت و دسترسی برای ما به عنوان یکی از انواع اعتبارسنجی اهمیت زیادی دارد.احراز هویت یعتی اینکه چه کسانی اجازه ورود به سیستم را دارند و مجوز یعنی بعد از ورود به چه بخش هایی دسترسی داشته باشد.
فیلتر امنیتی Authorize، کار محدود ساختن دسترسی به متدهای کنترلرها را انجام می‌دهد.اگر بالا اکشنی این فیلتر را بنویسیم به این معنا است که کاربران اعتبارسنجی نشده، امکان دسترسی به آن‌را نخواهند داشت. فیلتر Authorize همواره قبل از تمامی فیلترهای تعریف شده دیگر اجرا می‌شود.
فیلتر Authorize با پیاده سازی اینترفیس System.Web.Mvc.IAuthorizationFilter توسط کلاس System.Web.Mvc.AuthorizeAttribute در دسترس می‌باشد.

امکانات امنیتی بیشتر در MVC

برای پنهان سازی ورژن نرم افزاری که با آن اپلیکیشن را نوشته ایم در Application_Start در Global.asax تغییر زیر را انجام می دهیم .

public class HomeController : Controller  
{  
   public ActionResult Index()  
   {  
      return View();  
   }  
   [Authorize]  
   public ActionResult GetHome(){  
      return View()  
   }  
}  

در صورت بروز خطا در نرم افزار چه خطای امنیتی و چه خطاهای دیگر سه روش وجود دارد

1-On خطایی که MVC تولید می کند را به کاربر نشان نمی دهد و یک صفحه خطایی که خودمان طراحی کرده ایم به او نشان می دهد.

2-Off خطای رخ داده را با همان فرمت پیش فرض خود MVCنشان میدهد

3- Remote only ترکیبی از دو حالت قبل می باشد.

البته می توان کاربران را در صورت بروز خطا به صفحه پیش فرض مانند defaultredirect هدایت کرد.

نتیجه

با توجه به صحبت های گفته شده داشتن امنیت بر روی سایت و اعمال قوانین اعتبارسنجی از اهمیت ویژه ای برخوردار است .امروزه با توجه به آماری در روز 30000 سایت هک می شوند! واقعا عدد زیادی است.در حال حاظر دیتا های حساس زیادی بر روی شبکه اینترنت ذخیره می شوند.بنابراین ضروری است تا اعتبار سنجی با توجه به بحث هایی که انجام شد بر روی کدها اعمال شوند.

فایل های ضمیمه