یکی از مباحث بسیار مهم در دات نت مبحث امنیت (Security) است. زمانی که شما قصد توسعه یک نرم افزار را دارید علاوه بر پیاده سازی کلیات منطق برنامه، می بایست به نحوی برنامه را پیاده سازی کنید که امنیت اطلاعات برنامه نیز تضمین شود. این تضمین شامل کنترل دسترسی کاربران به بخش های مختلف برنامه و همچنین ذخیره سازی اطلاعات مهم به صورت رمزنگاری شده در بخش های ذخیره سازی اطلاعات مانند بانک اطلاعاتی است. در این مطلب به مورد اول یعنی کنترل دسترسی کاربرها به برنامه خواهیم پرداخت. در ابتدا با دو مفهوم اصلی در امنیت یعنی Authentication و Authorization آشنا می شویم :
در برنامه های دات نت نیز می توان پروسه های Authentication و Authorization را جهت کنترل دسترسی کاربران پیاده سازی کرد. برای این کار نیاز به آشنایی با مفاهیم Identity و Principal در دات نت داریم. زمانی که در برنامه های دات نت یک کاربر به سیستم وارد می شود بوسیله Identity اطلاعات آن کاربر را نگهداری می کنیم و در کنار آن، دسترسی های کاربر را بوسیله Principal مشخص می کنیم. دو اینترفیس IIdentity و IPrincipal در دات نت برای اینکار وجود دارند و البته کلاس هایی هم هستند که این اینترفیس را پیاده سازی کرده اند. این کلاس ها و اینترفیس ها در فضای نام System.Security.Principal قرار دارند:
علاوه بر کلاس های بالا، از نسخه 4.5 دات قابلیت جدیدی اضافه شده به نام Claims که می توان به صورت جدیدی Identity ها و Principal ها را استفاده کرد که در دات نت اصطلاحاً به آن WIF یا Windows Identity Foundation گفته می شود.
برای شروع ابتدا با کلاس های WindowsIdentity و WindowsPrincipal آشنا شده و اینکه چگونه برنامه خود را با حساب های کاربری ویندوز یکپارچه کنیم. برای بدست آوردن کاربر جاری ویندوز کافیست به صورت زیر عمل کنیم:
var identity = WindowsIdentity.GetCurrent(); Console.WriteLine(identity.Name);
خروجی دستورات بالا نام حساب کاربری ویندوز خواهد که بر روی سیستم من عبارت زیر نمایش داده می شود:
DESKTOP-CRF890N\Hossein Ahmadi
بعد از بدست آوردن حساب کاربری ویندوز از روش بالا، کلاس Principal را به صورت ایجاد کرده و استفاده می کنیم:
var identity = WindowsIdentity.GetCurrent(); Console.WriteLine(identity.Name); var principal = new WindowsPrincipal(identity); Console.WriteLine(principal.IsInRole("BuiltIn\\Users"));
همانطور که در کد بالا مشاهده می کنید برای ایجاد کلاس Principal به عنوان پارامتر سازنده کلاس WindowsPrincipal، شئ identity استفاده شده است. با اینکار و بوسیله کلاس WindowsIdentity می توانیم بررسی کنیم که یک کاربر دسترسی های مورد نظر را دارد یا خیر، برای نمونه در کد بالا بوسیله متد IsInRole بررسی کردیم که کاربر جاری عضو گروه Users در سیستم Local هست یا خیر. به عنوان مثال بعدی، در کد زیر لیست کلیه گروه هایی که کاربر در آن ها عضو می باشد را در خروجی نمایش می دهیم:
foreach(var group in identity.Groups) { Console.WriteLine(group.Value); }
با اجرای کد بالا خروجی نمایش داده شده حاوی یکسری کدها و اعداد نامفهوم است:
S-1-5-21-2167674382-1402212879-1573101476-513 S-1-1-0 S-1-5-114 S-1-5-21-2167674382-1402212879-1573101476-1002 S-1-5-32-544 S-1-5-32-562 S-1-5-32-578 S-1-5-32-559 S-1-5-32-545 S-1-5-4 S-1-2-1 S-1-5-11 S-1-5-15 S-1-5-113 S-1-2-0 S-1-5-64-10 S-1-5-32-4028125388-2803578072-1053907958-341417128-2434011155-477421480-740873757-3973419746 S-1-5-32-2745667521-2937320506-1424439867-4164262144-2333007343-2599685697-2993844191-2003921822 S-1-5-32-1034403361-4122601751-838272506-684212390-1217345422-475792769-1698384238-1075311541
در حقیقت خروجی که ما دریافت کردیم شامل شناسه گروه های موجود در سیستم می باشد. برای دریافت اسامی گروه ها کافیست کد بالا را به صورت زیر تغییر دهیم:
foreach(var group in identity.Groups) { Console.WriteLine(group.Translate(typeof(NTAccount))); }
بوسیله کد بالا و بوسیله متد Translate گروه مورد نظر را به کلاس NTAccount ترجمه کردیم و با اینکار به جای دریافت شناسه گروه ها نام گروه ها به صورت زیر در خروجی نمایش داده می شود:
Everyone NT AUTHORITY\Local account and member of Administrators group DESKTOP-CRF890N\HelpLibraryUpdaters BUILTIN\Administrators BUILTIN\Distributed COM Users BUILTIN\Hyper-V Administrators BUILTIN\Performance Log Users BUILTIN\Users NT AUTHORITY\INTERACTIVE CONSOLE LOGON NT AUTHORITY\Authenticated Users NT AUTHORITY\This Organization NT AUTHORITY\Local account LOCAL NT AUTHORITY\NTLM Authentication
متد Translate در موارد دیگری نیز کاربر دارد، برای مثال، در ابتدای مقاله کد زیر را برای بدست آوردن اطلاعات حساب کاربر جاری ویندوز استفاده کردیم:
var identity = WindowsIdentity.GetCurrent(); Console.WriteLine(identity.Name);
بوسیله کد زیر می توان شناسه حساب کاربری جاری را در سیستم بدست آورد:
var identity = WindowsIdentity.GetCurrent(); Console.WriteLine(identity.Name); var account = new NTAccount(identity.Name); Console.WriteLine(account.Translate(typeof(SecurityIdentifier)));
خروجی دستورات بالا به صورت زیر خواهد بود:
DESKTOP-CRF890N\Hossein Ahmadi S-1-5-21-2167674382-1402212879-1573101476-1001
برای پارامتر ورودی متد Translate می توان از دو کلاس SecurityIdentifier و NTAccount استفاده کرد که کلاس اولی برای بدست آوردن Identitfer گروه یا کاربر استفاده شده و کلاس دوم برعکس مورد اولی است که کاربرد هر دوی آن ها را در کد های بالا مشاهده کردید.همانطور که گفتیم از متد IsInRole برای بررسی دسترسی یک کاربر استفاده می شود. علاوه بر اینکه می توان نام دسترسی را به صورت مستقیم به متد IsInRole در کلاس WindowsIdentity ارسال کرد، امکان ارسال دسترسی مورد نظر به صورت کلاس SecurityIdentifier نیز وجود دارد. برای مثال، در کد زیر ما از کلاس SecurityIdentifier و همچنین enum ای با نام WellKnownSidType برای بررسی دسترسی مورد نظر استفاده کردیم:
var identity = WindowsIdentity.GetCurrent(); Console.WriteLine(identity.Name); var principal = new WindowsPrincipal(identity); var sid = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null); Console.WriteLine(principal.IsInRole(sid));
بوسیله کد بالا همان دسترسی BuiltinUsers را بوسیله کلاس SecurityIdentifier بررسی کردیم. در WellKnownSidType آیتم های دیگری نیز وجود دارد که می توان از آن ها برای بررسی دسترسی استفاده کرد. همانطور که در کد بالا مشاهده کردید، برای پارامتر دوم کلاس SecurityIdentifier مقدار null را استفاده کردیم. برای بررسی دسترسی در سطح domain می توان از از پارامتر دوم استفاده کرد. برای مثال، در کد زیر بررسی می کنیم که کاربر در سطح domain دسترسی مدیریتی دارد یا خیر:
var identity = WindowsIdentity.GetCurrent(); Console.WriteLine(identity.Name); var principal = new WindowsPrincipal(identity); var account = identity.User.AccountDomainSid.Translate(typeof(NTAccount)); var sid = new SecurityIdentifier(WellKnownSidType.AccountDomainAdminsSid, identity.User.AccountDomainSid); Console.WriteLine(principal.IsInRole(sid));
تا این لحظه با نحوه استفاده از کلاس های WindowsIdentity و WindowsPrincipal آشنا شدیم. اما اگر بخواهیم روند Authentication و Authorization را بر اساس حساب های کاربری که در خود برنامه تعریف شده پیاده سازی کنیم می بایست از کلاس های GenericIdentity و GenericPrincipal استفاده کنیم. استفاده از این کلاس ها خیلی ساده است، در نمونه کد زیر با فرض اینکه کاربری با نام Hossein از برنامه استفاده می کند و دسترسی های UsersAdmin و ProductsAdmin را دارد از کلاس های ذکر شده استفاده می کنیم:
var username = "Hossein"; var identity = new GenericIdentity(username); var roles = new[] { "UsersAdmin", "ProductsAdmin" }; var principal = new GenericPrincipal(identity, roles);
همانطور که مشاهده می کنید نام کاربری به عنوان پارامتر به کلاس GenericIdentity ارسال شده و شئ identity و متغیر roles نیز به کلاس GenericPrincipal ارسال شده اند. فراموش نکنید که مقادیر username و roles می بایست از بانک اطلاعاتی خوانده شوند و بعد از تائید هویت کاربر کلاس های بالا را ایجاد کنید. بعد از انجام کارهای بالا، در قدم بعدی می بایست Principal ایجاد شده را برای Thread جاری به صورت زیر ست کنید:
Thread.CurrentPrincipal = principal;
بعد از ست کردن Principal می توانیم روند بررسی دسترسی ها را به صورت زیر انجام دهیم:
public static void CheckPrincipal() { if (Thread.CurrentPrincipal.IsInRole("UsersAdmin")) { Console.WriteLine("welcome to Users Admin section."); } else { Console.WriteLine("You don't have permission to access this section!"); } }
با کد بالا مشکلی در اجرای کد وجود نخواهد داشت، به این خاطر که در زمان ساخت principal دسترسی UsersAdmin برای کاربر تعریف شده و کاربر می تواند به متد CheckPrincipal دسترسی داشته باشد. علاوه بر متد IsInRole می توان از Attribute ای به نام PrincipalPermission نیز برای کنترل دسترسی استفاده کرد. متد CheckPrincipal را در کد بالا بوسیله PrincipalPermission مجدد پیاده سازی می کنیم :
[PrincipalPermission(SecurityAction.Demand, Role = "Other")] public static void CheckPrincipal() { Console.WriteLine("Welcome"); }
با فراخوانی متد بالا پیغام خطایی به صورت زیر دریافت می کنیم :
Unhandled Exception: System.Security.SecurityException: Request for principal permission failed.
به این خاطر که دسترسی Other برای principal تعریف نشده است، اما اگر به جای Other از UsersAdmin استفاده می کردیم، متد بالا بدون مشکل فراخوانی می شود. علاوه بر اینکه می توان از PrincipalPermission به صورت Attribute استفاده کرد، امکان استفاده از آن به صورت مستقیم در کد نیز وجود دارد:
new PrincipalPermission(null, "Other").Demand();
بنیانگذار توسینسو و برنامه نویس و توسعه دهنده ارشد وب
حسین احمدی ، بنیانگذار TOSINSO ، توسعه دهنده وب و برنامه نویس ، بیش از 12 سال سابقه فعالیت حرفه ای در سطح کلان ، مشاور ، مدیر پروژه و مدرس نهادهای مالی و اعتباری ، تخصص در پلتفرم دات نت و زبان سی شارپ ، طراحی و توسعه وب ، امنیت نرم افزار ، تحلیل سیستم های اطلاعاتی و داده کاوی ...
زمان پاسخ گویی روز های شنبه الی چهارشنبه ساعت 9 الی 18
فقط به موضوعات مربوط به محصولات آموزشی و فروش پاسخ داده می شود