شروع دوره پایتون از پنجشنبه 1 خرداد ، مقدماتی تا پیشرفته، بدون پیش نیاز شروع دوره پایتون از پنجشنبه 1 خرداد ، مقدماتی تا پیشرفته، بدون پیش نیاز
🎯 ثبت نام

اعتبارسنجی در Net.-امنیت Net.

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

clip_image001[6]امنیت و اعتبار سنجی در .NET

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


clip_image003[3]


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

اعتبار سنجی سمت سرور (Server-Side Validation)

اجازه دهید با یک اعتبار سنجی ساده سمت سرور شروع کنیم. این نوع اعتبار سنجی زمانی لازم است که چیزی را برای سرور ارسال می کنیم و انتظار پاسخ داریم. مثلاً هنگام ارسال داده. ارسال داده ها در قالب form بسیار آسیب پذیر است. چرا که برای حمله کنندگان اینطوری راحت تر است. بنابراین باید صحت داده های دریافتی را در سرور بررسی کنیم، نه در سمت کلاینت. به این ترتیب با اعتبار سنجی سمت سرور می توانیم تا حدی از ورود داده های غیر منطقی جلوگیری کنیم. اجازه دهید نحوه انجام اعتبار سنجی صریح، با استفاده از view model را توضیح دهیم. در ابتدا چگونگی: صریح بودن یعنی اینکه در سمت سرور پس از ارسال فرم توسط کاربر بررسی شود. بسته به اینکه داده ورودی درست باشد یا غلط، پیغام مناسب پس از اعتبار سنجی به صورت پاسخ بازگردانده می شود. فرض کنید یک فرم برای ثبت نام کاربران در برنامه داریم. فرض کنیم یک model برای ثبت نام کاربران در برنامه داریم. کد آن به صورت زیر خواهد بود:

public class RegistrationViewModel(){ 

   public string FirstName { get; set; } 

   public string LastName { get; set; } 

   public string Address1 { get; set; } 

   public string Address2 { get; set; } 

   public string TelNo { get; set; } 

بنابراین در view باید label ها و text boxهای مربوطه نمایش داده شوند. صفحه view با موتور razor به شکل زیر خواهد بود.

model ServerSideValidationDemo.Models.RegistrationViewModel@ { 

    ViewBag.Title = "Registration Page"

 

using(Html.BeginForm()) {  

    Html.LabelFor(m => m.FirstName)@ Html.TextBoxFor(m => m.FirstName, new

        maxlength = 50 

    })@ Html.ValidationMessageFor(m => m.FirstName) 

    @ Html.LabelFor(m => m.LastName)@ Html.PasswordFor(m => m.LastName, new

        maxlength = 50 

    })@ Html.ValidationMessageFor(m => m.LastName) 

    @ Html.LabelFor(m => m.Address1)@ Html.PasswordFor(m => m.Address1, new

        maxlength = 50 

    })@ Html.ValidationMessageFor(m => m.Address1) 

    @ Html.LabelFor(m => m.Address2)@ Html.TextAreaFor(m => m.Address2, new

        maxlength = 200 

    })@ Html.ValidationMessageFor(m => m.Address2) 

    @ Html.LabelFor(m => m.TelNo)@ Html.TextBoxFor(m => m.MobileNo, new

        maxlength = 10 

    })@ Html.ValidationMessageFor(m => m.MobileNo) 

بنابر کد بالا، کاربر، به محلی که باید داده ها را وارد و بر روی دکمه submit کلیک کند، هدایت می شود. همانطور که می بینید در صفحه view موتور razor یک HTML Helper به نام ValidationMessageFor وجود دارد. وظیفه آن نمایش پیغام حاصل از اعتبار سنجی، سمت سروراست که به صورت یک پاسخ در ویژگی مربوطه در model قرار می گیرد.

برای مثال می خواهیم کاربر حتماً ویژگی ای در مدل به نام FirstName را وارد کند. پس از اعتبار سنجی، Helper پیغام حاصل از اعتبار سنجی را به همراه First Name Text Box نمایش می دهد. اجازه دهید نگاهی به کد Action بیاندازیم. جایکه با فشردن دکمه Submit فراخوانی می شود.

 

[HttpPost] 

public ActionResult UserRegistration(RegistrationViewModel registerModel) { 

    if (string.IsNullOrEmpty(registerModel.FirstName)) { 

        ModelState.AddModelError("FirstName", "Please enter your first name"); 

    } 

    if (!string.IsNullOrEmpty(registerModel.TelNo)) { 

        Regex telNoRegex = new Regex("^9\d{9}$"); 

        if (!telNoRegex.IsMatch(registerModel.TelNo)) 

            ModelState.AddModelError("TelNo", "Please enter correct format of Telephone Number"); 

    } 

    if (ModelState.IsValid) { 

        return View("Sucess"); //Returns user to success page 

    } else

        return View(); //Returns user to the same page back again 

    } 

پیش از توضیح کد بالا، بیاییم بفهمیم چطور پس از فشردن کلید Submit، فراخوانی صورت میگیرد. این کار با استفاده از @using (Html.BeginForm()) و بدون تعیین Action و controller انجام می شود. درواقع به صورت داخلی متد POST آدرس جاری را فراخوانی میکنیم. با این کار به دنبال ویژگی HttpPost، در نام action آدرس جاری می گردیم و متد post مربوط به UserRegistration فراخوانی کرده و view model مورد نیاز به action ارسال می شود و مقادیر ورودی کاربر برداشته می شوند. پس از فراخوانی متد Action Result، باید ویژگی ها صریحاً بررسی شوند. در اینجا ما بررسی می کنیم آیا کاربر نام را وارد کرده است یا نه. اگر وارد نکرده و بر روی دکمه submits کلیک کرده باشد، یک پیغام با این عبارت برای کاربر ارسال می کنیم: " Please enter the first name". این بررسی اعتبار سنجی به کاربر اجازه نمی دهد فرم را ارسال کند، مگر اینکه نام را وارد کرده باشد. مشابهاً، در مورد تلفن، برای تلفن هندوستان، نیز با عبارات معینی (regular expression) بررسی میشود. این ها همه برای وقتی است که اعتبارسنجی صراحتاً انجام شود. از آنجا که ما یک برنامه تحت MVC را توسعه می دهیم، یکسری ویژگی های از پیش تعیین شده برای بررسی داده های ارسالی وجود دارد. به این ویژگی ها، ویژگی های Data-Annotations (داده های حاشیه نویسی) می گویند. نگاهی به کاربرد آنها می اندازیم. Data-Annotations زمانی استفاده می شود که نخواهیم controller، actionای را بدون بررسی صریح هر ویژگی، ارسال کند. ویژگی های data annotation در یک view model مشابه زیر خواهند بود:

public class RegistrationViewModel() { 

    [Required] 

    [Display(Name = "First name")] 

    [StringLength(50, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)] 

    public string FirstName { 

        get

        set

    } 

    public string LastName { 

        get

        set

    } 

    public string Address1 { 

        get

        set

    } 

    public string Address2 { 

        get

        set

    } 

    [Required(ErrorMessage = "Please Enter Telephone Number")] 

    [Display(Name = "Tele Phone")] 

    [RegularExpression("" ^ 9\ d { 

            9 

        } 

        $ "", ErrorMessage = "Please Enter Correct format of Telephone No.")] 

    public string TelNo { 

        get

        set

    } 

 

در view model فوق از data annotation استفاده شده و تمام اعتبارسنجی های لازم برای تک تک ویژگی ها انجام می شود. اجازه دهید هر یک را جداگانه توضیح دهیم:

Required: این خاصیت کاربر را مجبور می کند که حتماً یک مقدار برای ویژگی مورد نظر وارد کند و بعد از آن فرم را submit کند. در غیر اینصورت پیغام " The FirstName is required" نمایش داده می شود. دقت داشته باشید که پیغام قبلی یک پیغام خطای پیش فرض بود. درحالیکه برای شماره تلفن، یک خطای سفارشی شده نمایش داده می شود.

Display (Name=): این خاصیت، برچسبی را برای ویژگی model تنظیم میکند. فقط کافیست @Html.LabelFor(m=>m.TeleNo) تعیین کنیم. با این کار برای ویژگی، برچسب مناسب نمایش داده می شود.

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

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

بنابراین صفحه view مانند بالا خواهد بود. زمانی که submit می کنیم، action متد post، با کدهای کمتری فراخوانی می شود. به action زیر دقت کنید.

[HttpPost] 

public ActionResult UserRegistration(RegistrationViewModel registerModel){ 

if (ModelState.IsValid) 

   { 

      return View("Success");//Return to the success  

   } 

   else 

   { 

      return View();//Return back to the same view  

}  

تا اینجا دیدید که data annotation چقدر باعث راحتی می شود. دراینجا یک آسیب پذیری امنیتی دیگر مطرح می شود: جعل درخواست میان وبگاهی (Cross-Site Request Forgery Attacks)، که می تواند با یک Fiddler ساده مورد حمله قرار بگیرد. وقتی داده ای ارسال می شود، به سادگی می توانیم داده های ارسال شده بوسیله کاربر را با استفاده از Fiddler پیاده سازی کرده و آن را در برنامه زائل کرده و حتی آن را از بین ببریم. این مورد می تواند خیلی خطرناک باشد. اجازه بدهید ببینیم چطور باید از حملات جعل درخواست میان وبگاهی، جلوگیری کنیم.

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

در صورت درک کامل موضوع در برنامه های تحت MVC، می توانیم از چنین درخواست هایی هنگام ارسال فرم داده جلوگیری کنیم. MVC ویژگی ای به نام [ValidatAntiForgeryToken] را در Action مربوطه، در اختیار شما قرار می دهد. بیایید نگاهی به قطعه کد زیر بیاندازیم. در ابتدا AntiForgeryToken helper را در صفحه razor view قرار می دهیم:

@using(Html.Form("UserRegistration", "Register")) {  

   @Html.AntiForgeryToken()  

   //Then here normal rest form as mentioned above  

سپس در کنترلری با نام Register و در Actionای با نام UserRegistration (POST)، به شکل زیر ویژگی را اضافه می کنیم:

[HttpPost] 

[ValidateAntiForgeryToken] 

public ActionResult UserRegistration(RegistrationViewModel registerModel){ 

   //ModeState Valid check and rest code goes here 

حالا بیایید بفهمیم چطور کار میکند؟ اگر این ویژگی ها را جایگذاری نکنیم چه اتفاقی می افتد؟ چقدر کنترلر ما آسیب پذیر است؟ با چه وسعتی برنامه ما قابل حمله خواهد بود. فرض کنیم ما در یک صفحه ویرایشگر قرار داریم و می خواهیم بخشی از اطلاعات login را ویرایش کنیم. حالا یک مهاجم از یک دامنه شخص ثالث، با یک HTML ساده، یکسری اطلاعات به همان Edit action ای که کاربر در آن بوده است، ارسال می کند. سپس اگر کاربر به هر ترتیب به Html مهاجم منتقل شود، کاربر بدون آگاهی، یکسری داده های ناخواسته را به سرور ارسال می کند و داده ها به صورت عادی در پایگاه داده ذخیره می شوند. ممکن است مهاجم شناسه های email یا هر گونه اطلاعات ناخواسته دیگری را به نفع خودش جابجا کند و به این ترتیب داده های کاربر را دریافت کند و این اصلاً خوب نیست. کاری که باید انجام دهیم این است که بررسی کنیم، آیا دامنه ای که درخواست از آن به action سرور ارسال شده و دامنه ای که کاربر با آن log in کرده است، یکی هستند. به همین دلیل باید چیزی مثل هدر و یا ویژگی ای که در زمان ساخت درخواست نگاشت شود داشته باشیم که اگر همخوانی داشت، ارسال انجام شود و در غیر اینصورت احراز هویت با شکست مواجه شود. این دقیقاً همان کاری است که ValidatAntiForgeryToken انجام می دهد. درواقع ValidatAntiForgeryToken یک cookie به نام RequestVerificationToken ایجاد می کند. با استفاده از بررسی اعتبار سنجی، به کاربر اجازه داده نمی شود ورودی را ارسال کند، مگر اینکه فیلدهای ضروری وارد شوند. شماره تلفن نیز با regular expression اعتبار سنجی می شوند. تمام این اعتبار سنجی ها باید صراحتاً انجام شوند. از آنجا که برنامه خود را تحت MVC توسعه می دهیم، می توانیم از یکسری ویژگی های از پیش تعیین شده برای اعتبار سنجی داده های ارسالی استفاده کنیم. به این ویژگی ها، ویژگی های Data-Annotations (داده های حاشیه نویسی) می گویند. نگاهی به کاربرد آنها می اندازیم. Data-Annotations زمانی استفاده می شود که نخواهیم post action یک controller خیلی سنگین شود هر ویژگی صریحاً بررسی شود.

یکی دیگر از حملات ناخواسته SQL Injection خواهد بود. اجازه دهید کمی درباره آن بحث کنیم.

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

حملات SQL Injection چیست؟ SQL Injection یک حمله احمقانه است که هدف آن دستکاری پایگاه داده می باشد. این کار با وارد کردن ورودی مخرب توسط کاربر، از طریق متدهای post و در صورت اعتبار سنجی نکردن داده های پست شده قبل از اجرا، به عنوان sql query، انجام می شود.

در صورتی که به مهاجمین اجازه دهیم با یک کوئری ساده، به همه ی داده های حساس دسترسی داشته باشند یا حتی رکورد ها را از جداول پاک کنند. در اینصورت هدف مهاجمین این است که کوئری های خودشان را در برنامه ارسال کنند و اجازه دهند سرور آن را اجرا کنند و اگر پاسخی از سمت سرور وجود نداشته باشد، به آن پاسخ دهد. اجازه دهید ببینیم چطور این کار انجام می شود. فرض کنیم یک کوئری sql داریم که نام خانه ها را انتخاب کرده و نمایش می دهد. این کوئری به شکل زیر است:

var sqlTobeExecuted = "SELECT HouseID, HouseName"

"FROM House "

"WHERE Address LIKE '" + searchInputParam +"%'; 

SqlDataAdapter da = new SqlDataAdapter(sqlTobeExecuted , DbCommand); 

کوئری فوق، یک کوئری ساده به صورت یک رشته و فاقد پارامتر ورودی است که مشخصات خانه ها را بر میگرداند. حالا فرض کنیم مهاجمی از طریق textbox ورودی کاربر، محتویات searchInputParam را به شکل زیر وارد کند:

' UNION SELECT id,name FROM sysobjects;--

به شکل تبدیل یافته دستور پس از ارسال به رشته ی کوئری توجه کنید:

SELECT HouseID, HouseName FROM House WHERE Address LIKE '' UNION SELECT id,name FROM sysobjects;--%' 

اولین apostrophe (') در searchInputParam پارامتر Like را در کوئری SQL می بندد و دو خط تیره (--) بقیه کوئری را به یادداشت تبدیل می کند. با این کار لیستی از HouseNameها و تمام جداول موجود در پایگاه داده بر میگرداند. حتی می تواند شناسه های sysObject را برداشته و با استفاده از آن ها می تواند هر کدام از ستون های جدول های پایگاه داده را که می خواهد، بر میدارد. فرض کنیم جدولی به نام Users داریم که همه ی اطلاعات کاربران در آن قرار دارد. حالا مهاجمین با دانستن شناسه و نام کاربران می توانند ستون اسامی را با استفاده از کوئری زیر، از جدول Users بردارند:

' UNION SELECT name FROM syscolumns WHERE id = "USERID";--

به این ترتیب کل پایگاه با یک کلیک ساده در معرض خطر قرار می گیرد. برای جلوگیری از این حملات:

داده های حساس و ضروری مانند کلمه عبور، اطلاعات کارت اعتباری و سایر جزئیات را رمزنگاری (Encrypt) کنید. به این ترتیب اگر به هر شکلی به این جزئیات دسترسی پیدا کنند، نمی توانند آنها را رمزگشایی (Decrypt) کنند. به عنوان مثال برای رمزنگاری رکوردهای داده از روش hashing می توانید استفاده کنید.

به جای رشته هایی مانند کوئری فوق، از کوئری های پارامتریک استفاده کنید تا مهاجمین نتوانند مستقیماً مقادیر مورد نظرشان را از طریق ورودی کاربر، تزریق کنند. همانطور که در ادامه می بینید، یک کوئری پارامتریک از ورودی های مخرب جلوگیری می کند.

 

var commandToBeExecuted = "SELECT HouseID, HouseName FROM House"

"WHERE Address Like @Address"

SqlCommand cmd = new SqlCommand(commandToBeExecuted , conn); 

cmd.Parameters.Add("@Address",address); 

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

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

استفاده از Entity Framework و LINQ. مادامی که از از LINQ برای موجودیت ها استفاده می کنیم، هرگز از رویکردهای مبتنی بر رشته در کوئری ها استفاده نخواهیم کرد. به جای آن از مدل شی (object model) API استفاده می کنیم که در مقابل حملات تزریق به SQL حساس نیست.


احراز هویت و مجوزدهی

این دو مورد در هر برنامه ای از اهمیت ویژه ای برخوردار هستند و دو مبحث متفاوت از هم به شمار می روند. اما هر دو برای حل یک چیز مورد استفاده قرار می گیرند: امنیت. زمانی که یک برنامه امنیتی را توسعه می دهیم، ورود (login)، امری ضروری است. بنابراین احراز هویت کاربران در برنامه و مجوزدهی به کاربران، برای بخش های مختلف برنامه، یک مسئله به شمار می رود. البته در سرتاسر برنامه های MVC، Forms Authentication انجام می شود. در web.config تنظیمات به شکل زیر انجام شده است.

این تنظیمات برای احراز هویت کافی نیست و باید یکسری کارهای دیگر هم انجام دهیم. برای انجام این کار، درگیر پیکربندی های زیادی خواهیم شد. اما با بهره گیری از WebSecurity در MVC، امنیت به راحتی قابل پیاده سازی است. همچنین جداول و hashing نیز در اختیار ما قرار می گیرد. Hashing یکی از راه کارها است که از امنیت بالایی نیز برخوردار می باشد. مسئله بعدی پس از تنظیم احراز هویت، مجوزدهی است که در سطح controller، در سطح action، انجام می شود و بوسیله آن می توان دسترسی به بخش های مختلف را بررسی کرد. ویژگی Authorize فقط sessionها را بررسی می کند و ما از آن برای بررسی نقش ها و سطوح دسترسی به بخش های مختلف نیز استفاده می کنیم.

 [Authorize] 

public class HomeController : Controller 

   public ActionResult Index() 

   { 

      return View(); 

   } 

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

public class HomeController : Controller 

   public ActionResult Index() 

   { 

      return View(); 

   } 

   [Authorize] 

   public ActionResult GetHome(){ 

      return View() 

   } 

در اینجا فقط متد action، با نام GetHome مجوزدهی شده است و نه تمام controller. به این ترتیب احراز هویت و مجوزدهی فاکتورهای بسیار مهمی هستند که حتماً باید در نظر گرفته شوند.

سایر ملاحظات امنیتی در MVC

براساس توصیه های امنیتی در OWASP، بهتر است نسخه MVC و ASP.NET مورد استفاده را همیشه مخفی نگهداریم و هرگز این اطلاعات را از طریق هدر در معرض دید قرار ندهیم.

X-AspNet-Version 4.0.30319 X-AspNetMvc-Version 5.0 

باید اطلاعات نسخه را، که در که در تب Network جدول توسعه دهنده وجود دارد، مخفی کنیم. بیایید ببینیم چطور باید آن  را از هدر ASP.NET و ASP.NET MVC حذف کنیم.

نسخه ASP.NET: برای مخفی کردن نسخه ASP.NET تغییرات زیر را در Web.Config اعمال کنید:

قطعه کد فوق نسخه ASP.NET را مخفی می کند.

نسخه در ASP.NET MVC: برای مخفی کردن نسخه در ASP.NET MVC  تغییرات زیر را در متد Application_Start در فایل Global.asax اعمال میکنیم. قطعه کد مربوطه به شکل زیر است:


protected void Application_Start() 

   MvcHandler.DisableMvcResponseHeader = true

به این ترتیب نسخه ASP.NET MVC از هدر در تب Network مخفی می شود.

در آخر، اگر در برنامه یک استثناء کنترل نشده رخ دهد، صفحه ی زرد رنگ خطا نمایش داده می شود. به همین دلیل توصیه می شود همیشه یک صفحه برای مواقع بروز خطا در نظر بگیرید تا هنگام مواجه با استثناء، کاربر را به آن صفحه منتقل کنید. اجازه دهید ببینیم چطور می توانیم این کار را انجام دهیم: باید خطاهای Custom در Web.Config روشن باشند. سه حالت برای خطاهای Custom وجود دارد که عبارتند از:

On: از نمایش stack trace هنگام وقوع استثناءها خودداری می کند و اجازه می دهد صفحه خطای دلخواه به کاربر نمایش داده شود. این صفحه خطا به Client های Remote  و Local نمایش داده می شود.

Off:  توضیحات استثناء بعد از stack trace به کاربر نشان داده می شود. خطا و استثناء ASP.NET و stack trace به Clientهای Remote و Local نمایش داده می شود.

Remote only: از نظر توسعه دهندگان این بهترین گزینه است. چرا که صفحات پیغام خطای طراحی شده، فقط به Clientهای Remote نشان داده می شود. با این کار، کاربران محلی، از جمله توسعه دهندگان می توانند خطای ASP.NET را مشاهده کنند. این گزینه به صورت پیش فرض انتخاب شده است.

ویژگی دیگری که برای عنصر خطای سفارشی (custom error) مورد استفاده قرار می گیرد، defaultredirect است. از این ویژگی، هنگام بروز استثناء، برای انتقال کاربران به صفحه پیش فرض استفاده می شود.

با استفاده از قطعه کد زیر، می توان استثناءها را به صورت سراسری در سطح برنامه کنترل کرد:

protected void Application_Error(Object sender, EventArgs e) 

   Exception ex = Server.GetLastError(); //self explanatory gets the most recent error 

   Server.ClearError(); //self explanatory clears the error  

   //(Required to clear as otherwise user gets to see the default ASP.NET error handlers) 

   Response.Redirect(""); //default redirect.  

جمع بندی

امروزه امنیت و اعتبارسنجی، ویژگی های مهمی هستند که باید در هر برنامه ای پیاده سازی شوند. براساس مجله Forbes، روزانه 30000 سایت هک می شود. این رقم واقعاً عجیب است! خصوصاً اینکه در دنیای امروز اطلاعات حساس زیادی در ابر (cloud) ذخیره می شوند. ازآنجا که هک شدن داده های برنامه بسیار محتمل است، لذا امنیت موضوع مهمی به شمار می رود و باید با دقت کنترل شود. خوشبختانه در اینجا به نکات خوبی پرداختیم که در هر برنامه ای قابل پیاده سازی هستند.



 

1394/09/03 3744 1693
رمز عبور : tahlildadeh.com یا www.tahlildadeh.com
نظرات شما

نظرات خود را ثبت کنید...