در حالت پیشرفتهی تزریق وابستگیها در دات نت، با توجه به اینکه کار وهله سازی کلاسها به یک کتابخانه جانبی به نام IoC Container واگذار میشود، امکان یک سری دخل و تصرف نیز در این میان فراهم میگردد. برای مثال الان که ما میتوانیم یک کلاس را توسط IoC container به صورت خودکار وهله سازی کنیم، خوب، چرا اجرای متدهای آنرا تحت نظر قرار ندهیم. مثلا حاصل آنها را بتوانیم پیش از اینکه به فراخوان بازگشت داده شود، کش کنیم یا کلا تغییر دهیم. به این کار AOP یا Aspect orinted programming نیز گفته میشود.
واقعیت این است که یک چنین مفهومی از سالهای دور به نام Hooking در برنامههای WIN32 API خالص نیز وجود داشته است. Hookها یا قلابها دقیقا کار Interception دنیای AOP را انجام میدهند. به این معنا که خودشان را بجای یک متد ثبت کرده و کار ردیابی یا حتی تغییر عملکرد آن متد خاص را میتوانند انجام دهند. برای مثال اگر برای متد gethostbyname ویندوز یک Hook بنویسیم، برنامه استفاده کننده، تنها متد ما را بجای متد اصلی gethostbyname واقع در Kernel32 ویندوز، مشاهده میکند و درخواستهای DNS خودش را به این متد ویژه ما ارسال خواهد کرد؛ بجای ارسال درخواستها به متد اصلی. در این بین میتوان درخواستهای DNS را لاگ کرد و یا حتی تغییر جهت داد.
انجام Interception در دنیای دات نت با استفاده از امکانات Reflection و Reflection.Emit قابل انجام است و یا حتی بازنویسی اسمبلیها و افزودن کدهای IL مورد نیاز به آنها که به آن IL Weaving هم گفته میشود. اما در دنیای WIN32 انجام چنین کاری ساده نیست و ترکیبی است از زبان اسمبلی و کتابخانههای نوشته شده به زبان C.
برای ساده سازی نوشتن Hookهای ویندوزی، کتابخانهای به نام easy hook ارائه شده است که امکان تزریق Hookهای دات نتی را به درون پروسه برنامههای Native ویندوز دارد. این قلابها که در اینجا متدهای دات نتی هستند، نهایتا بجای توابع اصلی ویندوز جا زده خواهند شد. بنابراین میتوانند عملیات آنها را ردیابی کنند و یا حتی پارامترهای آنها را دریافت و مقدار دیگری را بجای تابع اصلی، بازگشت دهند. در ادامه قصد داریم اصول و نکات کار با easy hook را در طی یک مثال بررسی کنیم.
صورت مساله
میخواهیم کلیه درخواستهای تاریخ اکسپلورر ویندوز را ردیابی کرده و بجای ارائه تاریخ استاندارد میلادی، تاریخ شمسی را جایگزین آن کنیم.
از کجا شروع کنیم؟
ابتدا باید دریابیم که اکسپلورر ویندوز از چه توابع API ایی برای پردازشهای درخواستهای تاریخ و ساعت خودش استفاده میکند، تا بتوانیم برای آنها Hook بنویسیم. برای این منظور میتوان از برنامهی بسیار مفیدی به نام API Monitor استفاده کرد. این برنامهی رایگان را از آدرس ذیل میتوانید دریافت کنید:
اگر علاقمند به ردیابی برنامههای 32 بیتی هستید باید apimonitor-x86.exe را اجرا کنید و اگر نیاز به ردیابی برنامههای 64 بیتی دارید باید apimonitor-x64.exe را اجرا نمائید. بنابراین اگر پس از اجرای این برنامه، برای مثال فایرفاکس را در لیست پروسههای آن مشاهده نکردید، یعنی apimonitor-x64.exe را اجرا کردهاید؛ از این جهت که فایرفاکس عمومی تا این تاریخ، نسخه 32 بیتی است و نه 64 بیتی.
پس از اجرای برنامه API Monitor، در قسمت API Filter آن باید مشخص کنیم که علاقمند به ردیابی کدامیک از توابع API ویندوز هستیم. در اینجا چون نمیدانیم دقیقا کدام تابع کار ارائه تاریخ را به اکسپلورر ویندوز عهده دار است، شروع به جستجو میکنیم و هر تابعی را که نام date یا time در آن وجود داشت، تیک میزنیم تا در کار ردیابی لحاظ شود.
سپس نیاز است بر روی نام اکسپلورر در لیست پروسههای این برنامه کلیک راست کرده و گزینه Start monitoring را انتخاب کرد:
اندکی صبر کنید یا یک صفحه جدید اکسپلورر ویندوز را باز کنید تا کار ردیابی شروع شود:
همانطور که مشاهده میکنید، ویندوز برای ردیابی تاریخ در اکسپلورر خودش از توابع GetDateFormatW و GetTimeFormatW استفاده میکند. ابتدا یک تاریخ را توسط آرگومان lpDate یا lpTime به یکی از توابع یاد شده ارسال کرده و سپس خروجی را از آرگومان lpDateStr یا lpTimeStr دریافت میکند.
خوب؛ به نظر شما اگر این خروجیها را با یک ساعت و تاریخ شمسی جایگزین کنیم بهتر نیست؟!
نوشتن Hook برای توابع GetDateFormatW و GetTimeFormatW ویندوز اکسپلورر
ابتدا کتابخانه easy hook را از مخزن کد CodePlex آن دریافت کنید:
سپس یک پروژه کنسول ساده را آغاز کنید. همچنین به این Solution، یک پروژه Class library جدید را نیز اضافه نمائید. پروژه کنسول، کار نصب Hook را انجام میدهد و پروژه کتابخانهای اضافه شده، کار مدیریت هوکها را انجام خواهد داد. سپس به هر دو پروژه، ارجاعی را به اسمبلی EasyHook.dll اضافه کنید.
الف) ساختار کلی کلاس Hook
کلاس Hook واقع در پروژه Class library باید یک چنین امضایی را داشته باشد:
namespace ExplorerPCal.Hooks { public class GetDateTimeFormatInjection : IEntryPoint { public GetDateTimeFormatInjection(RemoteHooking.IContext context, string channelName) { // connect to host... _interface = RemoteHooking.IpcConnectClient<MessagesReceiverInterface>(channelName); _interface.Ping(); } public void Run(RemoteHooking.IContext context, string channelName) { } } }
ب) نوشتن توابع Hook
کار نوشتن قلاب برای توابع API ویندوز در متد Run انجام میشود. سپس باید توسط متد LocalHook.Create کار را شروع کرد. در اینجا مشخص میکنیم که نیاز است تابع GetDateFormatW واقع در kernel32.dll ردیابی شود.
public void Run(RemoteHooking.IContext context, string channelName) { GetDateFormatHook = LocalHook.Create( InTargetProc: LocalHook.GetProcAddress("kernel32.dll", "GetDateFormatW"), InNewProc: new GetDateFormatDelegate(getDateFormatInterceptor), InCallback: this);
ج) نحوه مشخص سازی امضای delegateهای Hook
اگر امضای متد GetDateFormatW به نحو ذیل باشد:
[DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto, SetLastError = true)] public static extern int GetDateFormatW( uint locale, uint dwFlags, // NLS_DATE_FLAGS SystemTime lpDate, [MarshalAs(UnmanagedType.LPWStr)] string lpFormat, StringBuilder lpDateStr, int sbSize);
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Auto, SetLastError = true)] private delegate int GetDateFormatDelegate( uint locale, uint dwFlags, SystemTime lpDate, [MarshalAs(UnmanagedType.LPWStr)] string lpFormat, StringBuilder lpDateStr, int sbSize);
private int getDateFormatInterceptor( uint locale, uint dwFlags, SystemTime lpDate, string lpFormat, StringBuilder lpDateStr, int sbSize) { }
د) نصب Hook نوشته شده
باید دقت داشت که هر دو برنامه نصاب Hook و همچنین کتابخانه Hook، باید دارای امضای دیجیتال باشند. بنابراین به برگه signing خواص پروژه مراجعه کرده و یک فایل snk را به هر دو پروژه اضافه نمائید.
سپس در برنامه نصاب، یک کلاس را با امضای ذیل تعریف کنید:
public class MessagesReceiverInterface : MarshalByRefObject { public void Ping() { } }
سپس در ابتدای برنامه نصاب، یک کانال Remoting باز شده (که آرگومان جنریک آن دقیقا همین نام کلاس MessagesReceiverInterface فوق را دریافت میکند)
var channel = RemoteHooking.IpcCreateServer<MessagesReceiverInterface>(ref _channelName, WellKnownObjectMode.SingleCall);
RemoteHooking.Inject( explorer.Id, InjectionOptions.Default | InjectionOptions.DoNotRequireStrongName, "ExplorerPCal.Hooks.dll", // 32-bit version (the same, because of using AnyCPU) "ExplorerPCal.Hooks.dll", // 64-bit version (the same, because of using AnyCPU) _channelName );
پارامتر و پارامترهای بعدی، اطلاعاتی هستند که به سازنده کلاس هوک ارسال میشوند. بنابراین این سازنده میتواند تعداد پارامترهای متغیری داشته باشد:
.ctor(IContext, %ArgumentList%) void Run(IContext, %ArgumentList%)
چند نکته تکمیلی مهم برای کار با کتابخانه Easy hook
- کتابخانه easy hook فعلا با ویندوز 8 سازگار نیست.
- برای توزیع هوکهای خود باید تمام فایلهای همراه کتابخانه easy hook را نیز توزیع کنید و فقط به چند DLL ابتدایی آن بسنده نباید کرد.
- اگر هوک شما بلافاصله سبب کرش پروسه هدف شد، یعنی امضای تابع API شما ایراد دارد و نیاز است چندین و سایت را جهت یافتن امضایی صحیح بررسی کنید. برای مثال در امضای عمومی متد GetDateFormatW، پارامتر SystemTime به صورت struct تعریف شده است؛ درحالیکه ویندوز ممکن است برای دریافت زمان جاری به این پارامتر نال ارسال کند. اما struct دات نت برخلاف struct زبان C یک value type است و نال پذیر نیست. به همین جهت کلیه امضاهای عمومی که در مورد این متد در اینترنت یافت میشوند، در عمل غلط هستند و باید SystemTime را یک کلاس دات نتی که Refrence type است، تعریف کرد تا نال پذیر شود و hook کرش نکرده یا اشتباه عمل نکند.
- زمانیکه یک هوک easy hook بر روی پروسه هدف نصب میشود، دیگر قابل unload کامل نیست و نیاز است برای کارهای برنامه نویسی و به روز رسانی فایل dll جدید، پروسه هدف را خاتمه داد.
- متد Run هوک باید همیشه در حال اجرا باشد تا توسط CLR بلافاصه خاتمه نیافته و هوک از حافظه خارج نشود. اینکار را توسط روش ذیل انجام دهید:
try { while (true) { Thread.Sleep(500); _interface.Ping(); } } catch { _interface = null; // .NET Remoting will raise an exception if host is unreachable }
- استفاده همزمان از API Monitor ذکر شده در ابتدای بحث و یک هوک نصب شده، سبب کرش برنامه هدف خواهد شد.
سورس کامل این پروژه را در اینجا میتوانید دریافت کنید
MVC Scaffolding #3
آشنایی با ساختار اصلی MVC Scaffolding
پس از نصب MVC Scaffolding از طریق NuGet به پوشه Packages مراجعه نمائید. در اینجا پوشههای MvcScaffolding، T4Scaffolding و T4Scaffolding.Core ساختار اصلی این بسته را تشکیل میدهند. برای نمونه اگر پوشه T4Scaffolding\tools را باز کنیم، شاهد تعدادی فایل ps1 خواهیم بود که همان فایلهای پاورشل هستند. مطابق طراحی NuGet، همواره فایلی با نام init.ps1 در ابتدا اجرا خواهد شد. همچنین در اینجا پوشههای T4Scaffolding\tools\EFRepository و T4Scaffolding\tools\EFDbContext نیز قرار دارند که حاوی قالبهای اولیه کدهای مرتبط با الگوی مخزن و DbContext تولیدی میباشند.
در پوشه MvcScaffolding\tools، ساختار قالبهای پیش فرض تولید Viewها و کنترلرهای تولیدی قرار دارند. در اینجا به ازای هر مورد، دو نگارش vb و cs قابل مشاهده است.
سفارشی سازی قالبهای پیش فرض Viewهای MVC Scaffolding
برای سفارشی سازی قالبهای پیش فرض از دستور کلی زیر استفاده میشود:
Scaffold CustomTemplate Name Template
Scaffold CustomTemplate View Index
اگر دستور فوق را اجرا کنیم، فایل جدیدی به نام CodeTemplates\Scaffolders\MvcScaffolding.RazorView\Index.cs.t4 به پروژه جاری اضافه میشود. از این پس کلیه فرامین اجرایی، از نسخه محلی فوق بجای نمونههای پیش فرض استفاده خواهند کرد.
در ادامه قصد داریم اندکی این قالب پیش فرض را جهت اعمال ویژگی DisplayName به هدر جدول تولیدی نمایش اطلاعات Tasks تغییر دهیم. در کلاس Task، خاصیت زمان موعود با ویژگی DisplayName مزین شده است. این نام نمایشی حین تولید فرمهای ثبت و ویرایش اطلاعات بکار گرفته میشود، اما در زمان تولید جدول اطلاعات ثبت شده، به هدر جدول اعمال نمیگردد.
[DisplayName("Due Date")] public DateTime? DueDate { set; get; }
// Describes the information about a property on the model class ModelProperty { public string Name { get; set; } public string DisplayName { get; set; } public string ValueExpression { get; set; } public EnvDTE.CodeTypeRef Type { get; set; } public bool IsPrimaryKey { get; set; } public bool IsForeignKey { get; set; } public bool IsReadOnly { get; set; } }
static string GetDisplayName(EnvDTE.CodeProperty prop) { var displayAttr = prop.Attributes.OfType<EnvDTE80.CodeAttribute2>().Where(x => x.FullName == typeof(System.ComponentModel.DisplayNameAttribute).FullName).FirstOrDefault(); if(displayAttr == null) { return prop.Name; } return displayAttr.Value.Replace("\"",""); }
اکنون برای اعمال متد GetDisplayName، متد GetEligibleProperties را یافته و به نحو زیر تغییر دهید:
results.Add(new ModelProperty { Name = prop.Name, DisplayName = GetDisplayName(prop), ValueExpression = "Model." + prop.Name, Type = prop.Type, IsPrimaryKey = Model.PrimaryKeyName == prop.Name, IsForeignKey = ParentRelations.Any(x => x.RelationProperty == prop), IsReadOnly = !prop.IsWriteable() });
اکنون قسمت هدر جدول تولیدی را در ابتدای فایل t4 یافته و به نحو زیر تغییر میدهیم تا از DisplayName استفاده کند:
<# List<ModelProperty> properties = GetModelProperties(Model.ViewDataType, true); foreach (ModelProperty property in properties) { if (!property.IsPrimaryKey && !property.IsForeignKey) { #> <th> <#= property.DisplayName #> </th> <# } } #>
PM> Scaffold Controller -ModelType Task -ControllerName TasksController -DbContextType TasksDbContext -Repository -Force
سفارشی سازی قالبهای پیش فرض کنترلرهای MVC Scaffolding
در ادامه قصد داریم کدهای الگوی مخزن تهیه شده را اندکی تغییر دهیم. برای مثال با توجه به اینکه از تزریق وابستگیها استفاده خواهیم کرد، نیازی به سازنده اولیه پیش فرض کنترلر که در بالای آن ذکر شده «در صورت استفاده از یک DI این مورد را حذف کنید»، نداریم. برای این منظور دستور زیر را اجرا کنید:
PM> Scaffold CustomTemplate Controller ControllerWithRepository
به این ترتیب فایل جدید CodeTemplates\Scaffolders\MvcScaffolding.Controller\ControllerWithRepository.cs.t4 به پروژه جاری اضافه خواهد شد. در این فایل چند سطر ذیل را یافته و سپس حذف کنید:
// If you are using Dependency Injection, you can delete the following constructor public <#= Model.ControllerName #>() : this(<#= String.Join(", ", Repositories.Values.Select(x => "new " + x.RepositoryTypeName + "()")) #>) { }
PM> Scaffold Controller -ModelType Task -ControllerName TasksController -DbContextType TasksDbContext -Repository -Force -ForceMode ControllerOnly
یا اگر نیاز به تغییر کدهای الگوی مخزن مورد استفاده است میتوان از دستور ذیل استفاده کرد:
Scaffold CustomScaffolder EFRepository
لیست Scaffolderهای مهیا با دستور Get-Scaffolder قابل مشاهده است.
روش جاری شباهت زیادی به استفاده از Context در React دارد:
Context provides a way to pass data through the component tree without having to pass props down manually at every level
// parent component providing 'foo' var Provider = { provide: { foo: 'bar' }, // ... }
// child component injecting 'foo' var Child = { inject: ['foo'], created () { console.log(this.foo) // => "bar" } // ... }
Using an injected value as the default for a prop //دریافت میکنیم props در قسمت child را در کامپوننت foo مقدار const Child = { inject: ['foo'], props: { bar: { default () { return this.foo } } } } Using an injected value as data entry //دریافت میکنیم data در قسمت child را در کامپوننت foo مقدار const Child = { inject: ['foo'], data () { return { bar: this.foo } } }
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <title>Dependency injection</title> </head> <body> <div id="app"> <button @click="counter++">Increment counter</button> <h2>Parent</h2> <p>{{counter}}</p> <div> <h3>Child</h3> <child></child> </div> </div> <script> const Child = { inject: ['counter_in_child'], template: `<div>Counter Child is:{{ counter_in_child }}</div>` }; new Vue ({ el: "#app", components: { Child }, provide() { return { counter_in_child: this.counter }; }, data() { return { counter: 0 }; } }); </script> </body> </html>
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <title>Dependency injection</title> </head> <body> <div id="app"> <button @click="counter++">Increment counter</button> <h2>Parent</h2> <p>{{counter}}</p> <div> <h3>Child</h3> <child></child> </div> </div> <script> const Child = { inject: ['counter_in_child'], template: `<div>Counter Child is:{{ counter_in_child.counter }}</div>` }; new Vue ({ el: "#app", components: { Child }, provide() { const counter_in_child={}; Object.defineProperty(counter_in_child,'counter',{ enumerable:true, get:()=>this.counter }) return { counter_in_child }; }, data() { return { counter: 0 }; } }); </script> </body> </html>
provide
and inject
are primarily provided for advanced plugin / component library use cases. It is NOT recommended to use them in generic application code امروزه یکی از بزرگترین دغدغههای فعالان حوزه آی تی، برقراری امنیت اطلاعات میباشد. با پدید آمدن بانکهای دادهای آماری و مالی، حساسیت مسئله صد چندان میشود. در ادامه چک لیستی را ارائه مینمایم که با کمک آن میتوانید تا حدود بسیار خوبی امنیت نرم افزار تحت وب خود را برقرار نمایید. در برخی از موارد مثالهایی از تکنولوژی مایکروسافت آورده شده است که این بدلیل تخصص نویسنده در تکنولوژیهای مایکروسافت میباشد. در صورتیکه شما از تکنولوژیها و زبانهای سورس باز بهره میبرید، میبایست معادل مورد ذکر شده را در زبان مورد استفاده خود بیابید .
ابتدا اجازه دهید مقداری با حملات آشنا شویم و سپس راه مقابله را در کنار هم بررسی نماییم.
مهمترین و خطرناکترین حملات سطح وب :
حمله XSS
این نوع حملات بدین صورت است که هکر با استفاده از فرمهای عمومی یا خصوصی (پنلهای سایت) اقدام به ثبت کدهای مخرب جاوااسکریپت درون دیتابیس شما مینماید. همانطور که میدانید پایه اصلی سیستمهای احراز هویت، ساخت فایل کوکی بر روی کامپیوتر کاربران میباشد. زمانی که مطلب ثبت شدهی هکر برای کاربران شما نمایش داده میشود، کدهای جاوا اسکریپت هکر روی مرورگر کاربر، اجرا شده و اطلاعات کوکیهای کاربر به راحتی برای سایت هکر ارسال میشود (معمولا هکر یک صفحه روی وب میسازد تا بتواند اطلاعات دریافتی از کدهای جاوا اسکریپت خود را دریافت و در جایی ذخیره کند).
حال هکر به راحتی کوکی را بر روی مرورگر خودش تنظیم میکند و بعد وارد سایت شما میشود. سیستم شما او را با کاربر شما اشتباه میگیرد و به راحتی هکر به اطلاعات پنل کاربری کاربر(ان) شما دست پیدا میکند.
حمله SQL Injection
این حمله معروفترین حمله است که تقریبا با قدرت میتوانم بگویم که درتکنولوژی ASP.Net با امکانات فوق العادهای که بصورت توکار در دات نت در نظر گرفته شده است، بصورت کامل به فراموشی سپرده شده است. فقط 2 تا نکتهی ریز هست که باید در کدهایتان رعایت کنید و تمام.
این حمله بدین صورت است که هکر یک سری دستورات SQL را در کوئری استرینگ، به صفحات تزریق میکند و بدین صورت میتواند در کدهای کوئری TSQL شما اختلال ایجاد کند و اطلاعات جداول شما را بدست بیاورد. در این نوع حمله، هکر از طریق باگ سطح کد نویسی کدهای نرم افزار، به دیتابیس حمله میکند و اطلاعاتی مثل نام کاربری و کلمهی عبور ادمین یا کاربران را میدزد و بعد میرود داخل پنل و خرابکاری میکند.
حمله CSRF
این حمله یکی از جالبترین و جذابترین نوع حملات است که هوش بالای دوستان هکر را نشون میدهد. عبارت CSRF مخفف Cross Site Request Forgery است (احتمالا دوستان ام وی سی کار، این عبارت برایشان آشناست).
در این نوع حمله هکر یک فایل برای کاربر شما از طریق ایمیل یا روشهای دیگر ارسال میکند و کاربر را به این سمت سوق میدهد که فایل را باز کند. کاربر یک فایل به ظاهر معمولی مثل عکس یا ... را میبیند و فایل را باز میکند. وقتی فایل باز میشود دیتای خاصی دیده نمیشود و گاهی هم اروری مبنی بر ناقص بودن فایل یا ... به کاربر نمایش داده میشود و کاربر فکر میکند که فایل، ناقص برای ارسال شده ...
اما در حقیقت با کلیک بر روی فایل و باز کردن آن یک درخواست POST از کامپیوتر کاربر برای سایت شما ارسال میشود و در صورتیکه کاربر در آن زمان در سایت شما لاگین باشد، سایت درخواست را با روی باز میپذیرد و درخواست را اجرا میکند. بدین صورت هکر میتواند درخواستهایی را به سرویسهای سایت شما که مثلا برای حذف یک سری داده است، ارسال کند و اطلاعات کاربر را حذف کند.
حمله Brute Force
در این حمله، هکر از یک سری برنامه برای ارسال درخواستهای مکرر به فرمهای سایت شما استفاده میکند و بدین صورت فرمهای عمومی سایت شما مورد حجوم انبوهی از درخواستها قرار میگیرد که این امر در بهترین حالت موجب ثبت کلی دیتای اسپم در دیتابیس شما و در بدترین حالت موجب داون شدن سایت شما میشود.
حمله DDOS
این نوع حمله مانند حمله Brute Force است؛ با این تفاوت که درخواست به همهی صفحات شما ارسال میشود و معمولا درخواستها از چندین سرور مختلف برای سایت شما ارسال میشوند و حجم درخواستها به قدری زیاد است که عملا سرور شما هنگ میکند و کاملا از دسترس خارج میشود. این نوع حمله در سطح کد راه حل زیادی ندارد و در سطح سرور و فایروال باید حل شود و حل آن هم بدین صورت است که درخواستهای بیش از حد طبیعی از یک آی پی خاص تشخیص داده شده و به سرعت، آی پی بلاک میشود و از آن به بعد درخواستهای آن آی پی در فایروال از بین میرود و دیگه به سرور نمیرسد.
حمله SHELL
شل فایلی است خطرناک که اگر بر روی سرور سایت شما آپلود و اجرا شود، هکر از طریق آن دسترسی کاملی به کل سرور سایت شما خواهد داشت. فایلهای دیگری با نام بکدور [1] نیز وجود دارند که نویسنده تمایل دارد آنها را نیز از نوع حمله SHELL معرفی نماید. این نوع از فایلها به مراتب بسیار خطرناکتر از فایلهای شل میباشند؛ تا جایی که ممکن است سالها هکر به سروی دسترسی داشته باشد و مدیر سرور کاملا از آن بی خبر باشد. اینجاست که باید شدیدا مراقب فایلهایی که روی سایت شما آپلود میشوند باشید. نویسنده به تمامی خوانندگان پیشنهاد مینماید، در صورتیکه نرم افزار حساسی دارند، حتما از سرور اختصاصی استفاده نمایند؛ چرا که در هاستهای اشتراکی که در آنها فضا و امکانات یک سرور بصورت اشتراکی در اختیار چندین سایت قرار میگیرد، وجود باگ امنیتی در سایر سایتهای موجود بر روی سرور اشتراکی میتواند امنیت سایت شما را نیز به مخاطره بیاندازد. نویسنده تهیهی سرور اختصاصی را شدیدا به توسعه دهندگان سایتهای دارای تراکنشهای بانکی بالا (داخلی یا خارجی) پیشنهاد مینماید. زیرا درگاه تراکنشهای بانکی بر روی آی پی هاست شما قفل میشوند و در صورتیکه سرور بصورت اختصاصی تهیه شده باشد، آی پی سرور شما فقط و فقط در اختیار شماست و هکر نمیتواند با تهیه هاستی بر روی سرور اشتراکی شما، به راحتی آی پی قفل شده در درگاه بانکی شما را در اختیار داشته باشد. بدیهی است تنها در اختیار داشتن آی پی سرور شما جهت انجام خرابکاری در درگاه بانکی شما کافی نیست. ولی به نظر نویسنده این مورد در بدترین حالت ممکن 30% کار هکر میباشد. البته بحث حمله شل به سطح مهارت متخصصان سرورها نیز بستگی دارد. نویسنده اظهار میدارد اطلاعات دقیقی از تنظیماتی که بتواند جلوی اجرای انواع شل و یا جلوی دسترسی فایلهای شل را بگیرد، ندارد. بنابراین از متخصصان این حوزه دعویت مینماید اطلاعاتی درباره این موضوع ارائه نمایند.
حمله SNIFF
در این نوع حملات، هکر پکتهای رد و بدل شدهی بین کاربران و سرور شما را شنود مینماید و به راحتی میتواند اطلاعات مهمی مثل نام کاربری و رمز عبور کاربران شما را بدست آورد.
چک لیست امنیتی پروژههای نرم افزاری تحت وب
- بررسی کامل ورودیهای دریافتی از فرمهای سایت؛ هم در سمت کلاینت و هم در سطح سرور .
- در تکنولوژی دات نت به منظور تمیز سازی ورودیها و حذف تگهای خطرناکی همچون تگ script، کتابخانهای با نام Microsoft.Security.Application وجود دارد. کتابخانههای سورس باز دیگری نیز وجود دارند که نمونه آن کتابخانه AntiXss [2] سایت نوگت [3] میباشد.
- بررسی کامل ورودیهای دریافتی از کوئری استرینگهای [4] سایت. اگر از ASP.Net MVC استفاده مینمایید، تا حدی زیادی نیاز به نگرانی نخواهد داشت، زیرا تبدیلات [5] در سیستم Model Binding انجام میپذیرد و این موضوع تا حد زیادی شما را در برابر حملات SQL Injection مقاوم مینماید.
- حتما در فرمهای عمومی سایتتان از تصویر کپچا با امنیت بالا استفاده نمایید. این موضوع جهت شناخت روباتها از انسانها میباشد و شما را در برابر حملات Brute Force مقاوم مینماید.
- حتما سیستم شخصی سازی صفحات ارور را فعال نمایید و از نمایش صفحات ارور حاوی اطلاعات مهمی مانند صفحات ارور ASP.Net جلوگیری نمایید. این موضوع بسیار حساس میباشد و میتواند نقاط ضعف نرم افزار شما را برای هکر نمایان کند. حتی ممکن است اطلاعات حساسی مانند نام بانک اطلاعاتی، نام کاربری اتصال به بانک اطلاعاتی و نام جداول بانک اطلاعاتی شما را در اختیار هکر قرار دهد.
- استفاده از ORM ها یا استفاده از پروسیجرهای پارامتریک. این موضوع کاملا شما را در برابر حملات SQL Injection مقاوم مینماید. کما اینکه ORM ها، سطحی از کش را بصورت توکار دارا میباشند و این موضوع در سرعت دستیابی به دادهها نیز بسیار تاثیر گذار است. از طرف دیگر بانک اطلاعاتی SQL نیز امکانات توکاری جهت کش نمودن پرس و جوهای [6] پارامتریک دارد.
- لاگ کردن ارورهای سطح کد و سطح روتینگ [7] . یکی از مهمترین خصیصههای پروژههای با کیفیت، لاگ شدن خطاهای سطح کد میباشد. این امر شما را با نقاط حساس و ضعفهای نرم افزار آگاه میسازد و به شما اجازه میدهد به سرعت در جهت رفع آنها اقدام نمایید. لاگ نمودن خطاهای سطح روتینگ شما را از فعالیتهای هکرها جهت یافتن صفحات لاگین و صفحات مدیریتی پنل مدیریتی سایت اگاه مینماید، همچنین شما را از حملات SQL Injection نیز آگاه مینماید.
- جلوگیری از ایندکس شدن صفحات لاگین پنل مدیریت سایت در موتورهای جستجو. بخش مهمی از عملیات هکر ها، قرار دادن روباتهای تشخیص رمز بر روی صفحات لاگین میباشد که به نوعی میتوان این نوع حملات را در دسته حملات Brute Force قرار داد. موتورهای جستجو یکی از ابزارهای مهم هکرها میباشد. عملیات هایی مانند یافتن صفحات لاگین پنل مدیریتی یکی از کاربردهای موتورهای جستجو برای هکرها میباشد.
- لاگ کردن ورود و خروج افراد به همراه تاریخ، زمان، آی پی افراد و وضعیت لاگین. با کمک این موضوع شما میتوانید ورود و خروج کاربران نرم افزار خود را کنترل نمایید و موارد غیر طبیعی و مشکوک را در سریعترین زمان مورد بررسی قرار دهید.
- استفاده از روالهای استاندارد جهت بخش "فراموشی کلمه عبور". همیشه از استاندارهای نرم افزارهای بزرگ پیروی نمایید. بدیهی است استاندارهای استفاده شده در این نرم افزارها بارها و بارها تست شده و سپس بعنوان یک روال استاندارد در همهی نرم افزارهای بزرگ بکار گرفته شده است. استاندارد جهانی بخش "فراموشی کلمه عبور" که در اغلب نرم افزارهای معروف جهان بکار گرفته شده است، عبارت است از دریافت آدرس ایمیل کاربر، احراز هویت ایمیل وارد شده، ارسال یک نامهی الکترونیکی [8] حاوی نام کاربری و لینک تنظیم کلمه عبور جدید به ایمیل کاربر. بهتر است لینک ارسال شده به ایمیل کاربر بصورت یکبار مصرف باشد. کاربر پس از کلیک بر روی لینک تنظیم کلمه عبور جدید، وارد یکی از صفحات سایت شده و میتواند کلمهی عبور جدیدی را برای خود ثبت نماید. در پایان، کاربر به صفحهی ورود سایت هدایت شده و پیامی مبنی بر موفقیت آمیز بودن عملیات تغییر کلمهی عبور به او نمایش داده میشود. البته روال ذکر شده حداقل رول استانداردی میباشد و میتوان در کنار آن از روالهای تکمیل کنندهای مانند پرسشهای امنیتی و غیره نیز استفاده نمود.
- قراردادن امکاناتی جهت بلاک نمودن آی پیها و غیر فعال نمودن حساب کاربری اعضای سایت. در نرم افزار باید این امکان وجود داشته باشد که آی پی هایی که بصورت غیر طبیعی در سایت فعالیت مینمایند و یا مکررا اقدام به ورود به پنل مدیریتی و پنل کاربران مینمایند را بلاک نماییم. همچنین در صورت تخلف کاربران باید بتوان حساب کاربری کاربر خاطی را مسدود نمود. این موضوع میتواند بسته به اندازه پروژه و یا سلیقه تیم توسعه بصورت خودکار، دستی و یا هر دو روش در نرم افزار در تعبیه شود.
- امن سازی سرویسهای ای جکس و چک کردن ای جکس بودن درخواست ها. حتما جلوی اجرای سرویسهای درون نرم افزاری از بیرون از نرم افزار را بگیرید. سرویسهای ای جکس یکی از این نوع سرویسها میباشند که در نرم افزارها جهت استفادههای داخلی در نظر گرفته میشوند. در این نوع سرویسها حتما نوع درخواست را بررسی نمایید و از پاسخگویی سرویسها به درخواستهای غیر ای جکسی جلوگیری نمایید. در ASP.Net MVC این امر توسط متد Request.IsAjaxRequest انجام میپذیرد .
- محدود کردن سرویسهای حساس به درخواستهای POST. حتما از دسترسی به سرویس هایی از نوع Insert,Update و Delete از طریق فعل GET جلوگیری نمایید. در ASP.Net MVC این سرویسها را به فعل POST محدود نموده و در ASP.Net Web API این سرویسها را به افعال POST,PUT و DELETE محدود نمایید.
- عدم استفاده از آی دی در پنلهای کاربران بالاخص در آدرس صفحات (کوئری استرینگ) و استفاده از کد غیر قابل پیش بینی مثل GUID به جای آن. حتی الامکان بررسی مالکیت دادهها در همه بخشهای پنلهای کاربری سایت را جهت محکم کاری بیشتر انجام دهید تا خدای نکرده کاربر با تغییر اطلاعات کوئری استرینگ صفحات نتوانند به دادههای یک کاربر دیگه دسترسی داشته باشند.
- حتی الامکان پنل مدیران را از کاربران بصورت فیزیکی جدا نمایید. این مورد جهت جلوگیری از خطاهایی است که ممکن است توسط توسعه دهنده در سطح سیستم مدیریت نقش رخ دهد و موجب دسترسی داشتن کاربران به بخش هایی از پنل مدیریتی شود.
- استفاده از الگوریتمهای کدگذاری ترکیبی و کد کردن اطلاعات حساس قبل از ذخیره سازی در بانک اطلاعاتی. اطلاعات حساسی مانند کلمات عبور را حتما توسط چند الگوریتم کدگذاری، کدگذاری نمایید و سپس درون بانک اطلاعاتی ذخیره نمایید.
- تنظیمات حساس نرم افزار را درون فایل web.config قرار دهید و حتی الامکان آنها را نیز کدگذاری نمایید. بصورتی که اطلاعات قابلیت دیکد شدن را داشته باشند.
- ساخت پروژه بصورت چند لایه. این موضوع جهت جلوگیری از دستیابی هکر به ساختار لایههای پروژههای شما میباشد. به بیان دیگر اگر نهایتا هکر بتواند به اطلاعات FTP هاست شما دست یابد، استفاده از تکنولوژی چند لایه در بدترین حالت هکر را از دستیابی به اطاعات لایههای زیرین نرم افزار باز میدارد. البته این کار برای هکرها غیر ممکن نیست، اما بسیار سخت و زمان بر میباشد.
- اشتراک گذاری اینترفیس در سرویسهای خارج برنامه ای و عدم اشتراک گذاری کلاس اصلی. این موضوع از دستیابی هکر به بدنه سرویسها و پیاده سازیهای آنها جلوگیری مینماید.
- استفاده از تکنیکهای مقابله با CSRF در همه سرویسهای POST. در ASP.NET MVC اتریبیوتی با نام AntiForgery جهت مقاوم سازی سرویسها از حملات CSRF وجود دارد. مکانیزم بدین صورت است که در تمامی فرمهای سایت یک کد منحصر به فرد تولید میگردد که همراه درخواست GET به کامپیوتر کاربر ارسال میشود و در هنگام ارسال درخواست POST به سرور، صحت کد مورد نظر بررسی شده و در صورت صحت، اجازهی اجرای سرویس به درخواست داده میشود. بدین صورت وقتی کاربر سایت شما فایل آلودهای را باز مینماید، در خواست ارسالی هکر که توسط فایل باز شده، به سرور سایت ما ارسال میگردد، فاقد کد منحصر به فرد بوده و از اجرای سرویس جلو گیری میشود.
- استفاده از سیستمهای مدیریت نقش امن مانند IDENTITY در ASP.Net MVC و یا استفاده از امکانات توکار دات نت در سیستمهای مدیریت نقش شخصی سازی شده [9] . بدیهی است امنیت این سیستمها بارها و بارها تست شده است.
- بررسی فرمت و پسوند فایلهای آپلود شده. توجه نمایید که بررسی پسوند فایلها کافی نبوده و فرمت فایلها نیز میبایست بررسی شود. حتی نویسنده پیشنهاد مینماید فایلها را به نوعهای مرتبطشان تبدیل [10] نمایید. در حوزه هک بایند نمودن انواع ویروس، تروجان، شل و بک دور [11] به فایلهای تصویری و متنی یک امر بسیار رایج است. بنابراین حساسیت زیادی روی این موضوع قرار دهید. نویسنده توصیه مینماید کتابخانههای کاملی برای این موضوع تدارک ببینید تا در تمامی پروژهها نیاز به ایجاد مجدد آنها نداشته باشید و سعی نمایید در هر پروژه این کتابخانهها را تکمیلتر و بهتر نمایید.
- تنظیم IIS جهت جلوگیری از اجرای فایلهای اجرایی در مسیر آپلود فایلها. شاید جمله بیان شده به نظر ترسناک و یا سخت برسد، اما این کار با نوشتن چند تگ ساده در فایل Web.Config به راحتی قابل انجام است و نیاز به هیچ نوع کدنویسی ندارد.
- آپلود فایلها در پوشه App_Data و دسترسی به فایلها از طریق سرویسهای خود شما. پوشه App_Data پوشهای امن است و دسترسی مستقیم از طریق آدرس بار مرورگر به فایلهای درون آن توسط IIS داده نمیشود و افراد فقط از طریق سرویسهای خود شما میتوانند به فایلهای داخل این پوشه دسترسی داشته باشند. بدین صورت در سرویسهای خود میتوانید با تبدیل نمودن [12] فایلها به نوع خودشان (تصویر. پی دی اف یا ...) هکر را نا امید نمایید. این موضوع شما را در مقابل حملات SHELL مقاوم مینماید.
- استفاده از تکنیکهای لاگین چند سطحی برای پنل ادمین. در این روش شما حتی با داشتن نام کاربری و کلمهی عبور ادمین، قادر نخواهید بود وارد پنل ادمین شوید. نویسنده ابزار میدارد که این روش، یک روش ابداعی میباشد که از ترکیبی از احرا هویت ساده توسط نام کاربری و کلمهی عبور به همراه تکنیکهای احراز هویت ایمیل و موبایل مدیریت سایت میباشد.
- استفاده از SSL بسیار اهمیت دارد. بالاخص اگر نرم افزار شما Service Oriented باشد و نرم افزار شما سرویس هایی جهت اتصال به اپلیکیشنهای خارجی مثل اپلیکیشن اندروید دارد. این مورد در صفحات لاگین نیز بسیار مهم است و موجب میشود نام کاربری و کلمه عبور کاربران شما بصورت هش شده بین کامپیوتر کاربر و سرور شما رد و بدل شود و عملا شنود پکتها فایده ای برای هکر نخواهد داشت، زیرا دادهها توسط الگوریتمهای امنیتی که بین سرور و مرورگر کاربران توافق میشود کدگذاری شده و سپس رد و بدل میشوند.
[1] Back Door
[2] https://www.nuget.org/packages/AntiXss/
[3] www. Nuget.org
[4] Query String
[5] Casting
[6] Procedure
[7] Routing
[8] Email
[9] Custom Role Provider
[10] Cast
[11] Back Door
[12] Cast
Professional REST API design with ASP.NET Core and WebAPI
This project is an example of lightweight and extensible infrastructure for building RESTful Web API with ASP.NET Core.
This example contains a number of tricks and techniques which I've learned while building APIs in ASP.NET Core.
Techniques and Features
- JWT Authentication
- Secure JWT using Encryption (JWE)
- Logging to File, Console and Database using Elmah & NLog
- Logging to sentry.io (Log Management System)
- Exception Handling using Custom Middleware
- Automatic Validation
- Standard API Resulting
- Dependency Injection using Autofac
- Map resources using AutoMapper
- Async/Await Best Practices
- Versioning Management
- Using Swagger (Swashbuckle)
- Auto Document Generator for Swagger
- Integrate Swagger and Versioning
- Integrate Swagger and JWT/OAuth Authentication
- Best Practices for Performance and Security
var list = new List<int> {1,1,2,2,3,3,3,4,4,4};
var hash = new HashSet<int>(); var duplicates = list.Where(i => !hash.Add(i));
var movies = new List<Movie> { new Movie("Titanic", 1998, 4.5f), new Movie("The Fifth Element", 1997, 4.6f), new Movie("Terminator 2", 1991, 4.7f), new Movie("Avatar", 2009, 5), new Movie("Platoon", 1986, 4), new Movie("My Neighbor Totoro", 1988, 5) }; var distinctRatings = movies.DistinctBy(movie => movie.Rating); // Output: // Titanic,The Fifth Element,Terminator 2,Avatar,Platoon
وبلاگها ، سایتها و مقالات ایرانی (داخل و خارج از ایران)
- تمامی کتابخانه های تقویم خورشیدی شامل ۴۱ کتابخانه
- قالب فارسی جدید برای بلاگر
- فایرفاکس ۳ با محیط کاملا فارسی و غلط یاب فارسی
- ضعفهای امنیتی فایرفاکس بسیار بیشتر از IE است. (31 مورد IE در مقابل 115 مورد فایرفاکس در سال 2008)
- آشنایی با مایکروسافت Search Server
- دانلود سرویس پک دوم ویندوز ویستا برای عموم آزاد شد
- ابزار ThreadWorker برای انجام کارها در پشت صحنه در دلفی
- نگاهی دیگر به مشخصات و مولفههای تکنیکی مرورگر کاربران ایرانی
- کارگاه برنامهنویسی وب
- چه زمانی از کنترلهای سفارشی در برنامهمان استفاده کنیم
امنیت
Visual Studio
ASP. Net
طراحی و توسعه وب
- آشنایی با JSONP
- از فلش استفاده نکنید!
- ارائه jQuery UI 1.7 و مصاحبهای با خالق آن
- آنچه که در مورد jQuery UI 1.7 باید بدانید.
- آیا HTML Validation اهمیت دارد؟
- بیش از 2500 آیکون مجانی
- فرمهای زیباتری را طراحی کنید
PHP
اسکیوال سرور
سی شارپ
VB
عمومی دات نت
- RestPad
- آشنایی با تعدادی پروژهی سورس باز دات نت
- Introducing ADO.NET Data Services 1.5
- DebuggerDisplay Attribute
جاوا
ویندوز
- Windows 7 Release Candidate Preview
- آیا IE8 آخرین مرورگر مایکروسافت خواهد بود؟
- آشنایی با DFS در ویندوز سرور 2008
مسایل اجتماعی و انسانی برنامه نویسی
SVN
متفرقه
- Singleton
- Scoped
- Transient
Singleton (یگانه)
فقط و فقط یک شیء از سرویس ثبت شده با این طول عمر، در اولین درخواست ایجاد میشود و سپس در کل طول حیات برنامه، از همین شیء ایجاد شده، استفاده میگردد. به همین دلیل به آن «یگانه» یا Singleton میگویند. هر زمانیکه این سرویس در خواست داده میشود، DI Container، همان یک شیء را در اختیار درخواست دهنده قرار میدهد و این شیء، هیچگاه از بین نمیرود. به بیان دیگر، DI Container هیچگاه این شیء را از بین نمیبرد. شیء ساخته شده از سرویس ثبت شدهی با حالت Singleton، بین تمامی استفاده کنندگان، به صورت اشتراکی استفاده میشود. این طول عمر تقریبا مشابهی اشیاء ساخته شده توسط Singleton Pattern عمل میکند.
با توجه به مطالب گفته شده، ویژگیهای سرویسهای Singleton به شرح زیر هستند:
- در اولین درخواست به سرویس، یک نمونه از آن ساخته میشود و تا پایان برنامه در حافظه نگه داشته میشود.
- در سایر درخواستها، همان یک نمونهی ساخته شدهی از سرویس، ارائه داده میشود.
- به علت موجود بودن در حافظه، معمولا دسترسی به آنها و عملکرد آنها سریعتر است.
- بار کاری بر روی Garbage Collector فریمورک را کاهش میدهند.
بنابراین در هنگام تعریف کردن یک سرویس به صورت Singleton باید نکات زیر را مد نظر قرار بدهید:
- باید سرویس مورد نظر Thread Safe باشد .
- نباید استفاده کنندهی از این سرویس، امکان تغییر State آن را داشته باشد.
- اگر ساخت شیءای از یک سرویس، هزینهی زیادی را داشته باشد ، احتمالا Singleton کردن آن میتواند ایدهی خوبی باشد.
- شیء ساخته شدهی از این سرویس، تا زمان اجرای برنامه، بخشی از حافظهی برنامه را اشغال میکند. پس باید حجم اشغالی در حافظه را نیز مد نظر قرار داد.
- تعداد دفعات استفاده را در برابر مصرف حافظه در نظر بگیرید.
services.AddSingleton(services => new GuidProvider());
public HomeController(ILogger<HomeController> logger, IMessageServiceA messageService, LiteDbConfig liteDbConfig, GuidProvider guidHelper) { _logger = logger; _messageService = messageService; _messageService = new MessageServiceAA(); _guidHelper = guidHelper; }
حالا اگر برنامه
را اجرا کنیم، میبینید که با تازه
سازی صفحهی Home/Index ، همچنان Id، برابر با یک رشتهی یکسان
است. حتی اگر تب دیگری را در مرورگر باز کنیم و دوباره به این صفحه برویم، میبینیم
که Id برابر همان
رشتهی قبلی است و دلیل این موضوع، ثبت
سرویس Guid Service به صورت Singleton است.
Scoped ( محدود شده )
به ازای هر درخواست (در اینجا معمولا درخواستهای Http مد نظر است) یک نمونه از این سرویس ساخته میشود و در طول حیات این درخواست، DI Container به هر کلاسی که به این سرویس نیاز دارد، همان یک نمونه را برگشت میدهد و این نمونه در کل طول اجرای این درخواست، بین تمامی سرویس گیرندگان، یکسان است. هر زمانی، درخواست به پایان برسد، نمونهی ساخته شده از سرویس، Disposed میگردد و GC میتواند آن را از بین ببرد.
معمولا سرویسهای اتصال به پایگاه دادهها و کار بر روی آنها که شامل خواندن، نوشتن، ویرایش، حذف میشوند را با طول حیات Scoped ، درون DI Container ثبت میکنند . EF Core به صورت پیش فرض ، Db Context را به صورت Scoped ثبت میکند.
سرویسهای Scoped در محدودهی درخواست، مانند Singleton عمل میکنند و شیء ساخته شده و وضعیت آن در بین تمامی سرویسهایی که به آن نیاز دارند، مشترک است. بنابراین باید به این نکته در هنگام تعریف سرویس به صورت Scoped ، توجه داشته باشید.
تمام Middleware های ASP.NET Core هم فقط همان نمونهی ایجاد شده از سرویس Scoped را در طی اجرای یک درخواست خاص، میگیرند.
هر سرویسی که به سرویسهای Scoped نیاز دارد، یا باید به صورت Transient و یا باید به صورت Scoped ثبت شود، تا مانع از این شویم که شیء ساخته شده، فراتر از طول حیات موردنظرمان، در حافظه بماند و از آن استفاده شود .
برای ثبت یک سرویس
به صورت Scoped میتوانیم از متدهای توسعهای با نام AddScoped() با سربارهای
مختلف بر روی IServiceCollection استفاده کنیم. در اینجا از نسخهای که دو
پارامتر جنریک را میگیرد، برای ثبت یک سرویس به صورت Scoped استفاده میکنیم:
services.AddScoped<IMessageServiceB, MessageServiceBA>();
می توانیم سرویس GuidProvider را به جای Signleton ، به صورت Scoped ثبت کنیم:
services.AddScoped(services => new GuidProvider());
Transient (گذرا)
به ازای هر درخواست دهندهی جدید، یک نمونهی جدید از سرویس، توسط DI Container ساخته میشود و در اختیار آن قرار میگیرد.
سرویسهایی را به این صورت ثبت کنید که:
- نیاز به Thread Safe بودن داشته باشند.
- نمی توانید طول عمر سرویس را حدس بزنید.
سرویسهای Transient ، کارآیی پائینتری دارند و سربار عملکردی زیادی بر روی Garbage Collector می گذارند؛ ولی به علت اینکه به ازای هر واکشی، یک نمونهی جدید از آنها ساخته میشود و State بین این اشیاء به اشتراک گذاشته نمیشود، امنیت بیشتری دارند و درک و خطایابی آنها سادهتر است.
برای ثبت سرویسهای Transient از متد توسعهای AddTransient() استفاده میکنیم. سربارهای این متد مانند سربارهای متدهای AddSingleton() و AddScoped() است:
services.AddTransient<IMessageServiceC, MessageServiceCA>();
وابستگیهای محصور شده
یک سرویس نباید وابستهی به سرویسی باشد که طول حیاتی کمتر از طول حیات خودش را دارد.
برای مثال اگر درون سرویسی با طول حیات Singleton، از یک سرویس با طول حیات Transient استفاده کنیم، اینکار باعث میشود که یک نمونه از سرویس Transient در طول حیات برنامه، همیشه درون حافظه بماند و این ممکن است باعث خطاهای عجیبی در هنگام اجرا شود که معمولا خطایابی و رفع آنها مشکل است.
اثرات جانبی وابستگیهای محصور شده:
- به اشتراک گذاری تصادفی وضعیت یک شیء بین Thread ها درون سرویسهایی که Thread Safe نیستند.
- اشیاء، بیش از زمان پیش بینی شدهی برایشان، درون حافظه باقی میمانند.
سرویسهای Transient میتوانند به سرویسهایی با طول حیات زیر وابستگی داشته باشند:
- Transient
- Scoped
- Singleton
سرویسهای Scoped میتوانند به سرویسهایی با طول حیات زیر وابستگی داشته باشند:
- Transient
- Scoped
سرویسهای Singleton میتوانند به سرویس هایی با طول حیات زیر وابستگی داشته باشند:
Singleton
میتوانید از جدول زیر به عنوان راهنمای خلاصه شدهی برای استفادهی امن از سرویسها درون یکدیگر بهره ببرید:
Scope Validation
این قابلیت که به صورت پیش
فرض در حالت توسعهی برنامههای ASP.NET Core فعال است، در زمان شروع برنامه و در Startup ، بررسی میکند که سرویسها، وابستگی
به سرویسهایی با طول حیاتی مناسب، داشته باشند.
Unit of Work یا الگوی کار در واقع یک الگو، جهت جمع آوری عملیات کار با دیتابیس است که همه عملیات را تحت یک تراکنش به سمت دیتابیس ارسال میکند تا مبحث Atomic بودن عملیات، به مرحله اجرا گذاشته شود. در صورتیکه یکی از عملیات با نقص یا خطایی روبرو شود، کل عملیات Roll back یا برگشت میخورد. از آنجا که دیتابیسهای معدودی چون Ravendb این مراحل را تا حدی پیاده سازی میکنند نباید از مونگو هم چنین انتظاری نداشته باشید. مونگو برخورد تراکنشی یا اتمیک ندارد؛ پس پیاده سازی الگوی واحد کاری تاثیری بر روی روند کاری آن ندارد. هر چند تعدادی مثال بدین شکل پیاده شدهاند، ولی در عمل حقیقی نیستند و تنها یک حرکت مشابه داشتهاند.
ولی الگوی repository برای پرهیز از تکرار کد، قابلیت به روزرسانی کد و همچنین عملیاتی چون آزمونهای واحد و چهارچوب تقلید به کار میرود. وابستگی بین اشیاء را کاهش داده و باعث ایجاد یک کد با دوامتر میگردد.
ابتدا قبل از هر چیزی نیاز است تا اتصالات یا ساخت کانکشن به سرور و همچنین دریافت دیتابیس مورد نظر را در قالب یک کلاس تعریف نماییم. نام آن را MongoDbContext میگذارم:
public class MongoDbContext : IMongoDbContext { public const string DatabaseName = "MongoDbTest"; private static readonly IMongoClient _client; private static readonly IMongoDatabase Database; static MongoDbContext() { _client = new MongoClient(); Database = _client.GetDatabase(DatabaseName); } public IMongoCollection<TEntity> GetCollection<TEntity>() { return Database.GetCollection<TEntity>(typeof(TEntity).Name.ToLower() + "s"); } }
public interface IMongoDbContext { IMongoCollection<TEntity> GetCollection<TEntity>(); }
در مرحله بعد یک IMongoDbRepositry ساخته و محتوای آن را به شکل زیر پر میکنیم:
public interface IMongoDbRepository { Task<List<TEntity>> GetMany<TEntity>(FilterDefinition<TEntity> filter) where TEntity : class, new(); }
public class MongoRepository : IMongoDbRepository { private IMongoDbContext _mongoDbContext ; public MongoRepository(IMongoDbContext mongoDbContext) { _mongoDbContext = mongoDbContext; } public async Task<List<TEntity>> GetMany<TEntity>(FilterDefinition<TEntity> filter) where TEntity : class, new() { var collection = GetCollection<TEntity>(); var entities = await collection.Find(filter).ToListAsync(); return entities; } private IMongoCollection<TEntity> GetCollection<TEntity>() { return _mongoDbContext.GetCollection<TEntity>(); } }
الگوی بالا در یک کنترلر به شرح زیر استفاده شده است:
public class HomeController : Controller { private IMongoDbRepository _mongoDbRepository; public HomeController(IMongoDbRepository mongoDbRepository) { this._mongoDbRepository = mongoDbRepository; } // GET: Home public async Task<ActionResult> Index() { var filter = Builders<Resturant>.Filter.Gte("Capacity", 400); var c =await _mongoDbRepository.GetMany<Resturant>(filter); return View(c); } }
MongoRepository.zip