PDA

View Full Version : آموزش: تعیین اعتبار با استفاده از FormsAuthentication



ahmad156
یک شنبه 03 شهریور 1392, 19:15 عصر
با سلام خدمت دوستان
مدتی هست دارم روی این موضوع کار میکنم .خیلی توی این سایت و سایت های دیگه گشتم ولی مطالب خیلی پراکنده و ناقص بود.مطالبی رو به صورت یکجا جمع کردم.امیدوارم به دردتون بخوره.
در ابتدا با مفاهیم آشنا میشیم و در انتها بحث رو با پروژه نمونه پایان میدیم.
از اساتید عزیز هم خواهش دارم این بحث رو کامل تر کنن.


FormsAuthenticationTicket
کلاسی جهت نمایش بلیط تصدیق(authentication ticket) می باشد که در انتها تبدیل به یک String می شود که در Value کوکی ذخیره می شود. که دارای 3 Overload می باشد.مورد زیر برای کار ما کفایت می کند:




FormsAuthenticationTicket(ticket version, user name, issueDate,expiration,isPersistent, user Data) (http://msdn.microsoft.com/en-us/library/c9ed7kyz.aspx)



1-Ticket Version


در .NET 1.1 مقدار 1 رو برمیگردونه و در .Net 2.0 به بالا مقدار 2 و مقدار پیش فرض اون 2 هست.
توجه*اگر مقداری غیر از 1 یا 2 نسبت دهید مقدار پیش فرض را در نظر میگیرد.


2-UserName


همان نام کاربری هست.


3-IssueDate


این هم که زمان ایجاد ticket هست که معمولا مقدار DateTime.Now رو میگیره.


4-Expiration


این هم که زمان حذف ticket هست که معمولا از تابع Add مربوط به DateTime.Now استفاده میکنیم.


5-IsPersistent


آیا بلیط تصدیق مانا باشد یا نه(آیا با بستن مرورگر بلیط از بین رود یا، نه در بازدید های بعدی هم موجود باشد)
توجه*
1-اگر مقدار false بگیره این معتی رو میده که وقتی مرورگر بسته شد از بین می رود و تمام.
2-اگر expiration مربوط به ticket کمتر از expiration مربوط به خود کوکی باشد(یا بالعکس) در زمان کمتر، کوکی از بین می رود.


6-UserData

یکی از مشکلاتی که کلاس FormsAuthenticationTicket دارد عدم پشتیبانی مستقیم از Role هاست.برای تعیین نقش ها شما باید از UserData استفاده کنید.در واقع UserData یک String بیشتر نیست و جایی بیش برای ذخیره عنوان نقش ها نیست و شما باید در Application_AuthenticateRequest نقش ها را از UserData بازیابی کرده و به وسیله کلاس GenericPrincipal به Context.User نسبت دهید.

ahmad156
یک شنبه 03 شهریور 1392, 19:19 عصر
حال پیاده سازی موارد بالا:
مقدار expiration مربوط به ticket را مقدارDateTime.MaxValue قرار دهید.سپس isPersistent را true مقداردهی کنید.سپس گزینه "مرا به خاطر بسپار" را بررسی میکنیم و expire مربوط به کوکی را به صورت زیر لحاظ میکنیم:


Expires =chRemeberMe.Checked ? ticket.Expiration : DateTime.MinValue

معنای عبارت بالا :
اگر کاربر گزینه "مرا به خاطر بسپار" را انتخاب کرده تا تاریخی که FormsAuthenticationTicket برای expiration انتخاب کرده(ما یک سال را انتخاب کردیم) هویت کاربر در سیستم شناخته شده می باشد در غیر این صورت با بستن مرورگر هویت کاربر در سیستم پاک خواهد شد.
تا الان ما توانستیم کوکی که محتوای یک شی از کلاس FormsAutheticationTicket باشد را بسازیم.حال این سوال مطرح هست که چگونه از محتوای این کوکی جهت تعیین هویت استفاده کنیم
if (!FormsAuthentication.CookiesSupported)
throw new Exception("Cookieless Forms Authentication is not supported for this application.");
var ticket = new FormsAuthenticationTicket(2, txtUserName02.Text, DateTime.Now, DateTime.MaxValue, true, "admin");
string encryptTicket = FormsAuthentication.Encrypt(ticket);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptTicket)
{
Expires = chkRememberMe.Checked ? ticket.Expiration : DateTime.MinValue
};


Response.SetCookie(cookie);


نکات:
1-در ابتدا بررسی میکنیم که اگر مرورگر کاربر از کوکی پشتیبانی نمیکند خطایی رخ دهد.(گرچه میتوان بدون استفاده از کوکی و با استفاده از URL این کار را انجام داد ولی به خاطر امنیت کم از این کار انصراف میدهیم)
2-استفاده از encrypt و decrypt ضریب امنیت کار را بالا می برد.

ادامه دارد ....

ahmad156
دوشنبه 04 شهریور 1392, 18:06 عصر
Global.asax


در ASP.NET پردازش هر درخواست دارای مراحل یا فازهای مختلف است و در هر فاز رویدادهای مشخصی وجود دارد.این رویداد ها در کلاس فوق در دسترس هستند(شما میتوانید با HttpModule هم رویدادهای کلاس فوق را پیاده سازی کنید).بحث این کلاس جدا.

حال رویدادی که ما به آن نیاز داریم رویداد Application_AuthenticateRequest می باشد.این رویداد به ازای هر درخواست منبع از سرور(شامل خود صفحه، عکس ، فایل css ، فایل js و ...) اجرا می شود.

در هر درخواست که از طرف Client به طرف سرور ارسال می گردد کوکی مربوطه هم ارسال می گردد(بر اساس دامین و مسیر(Domain & Path))

پس شما قادر خواهید بود از Context.Request کوکی مورد نظرتون رو با توجه به نامی که در web.config تنظیم کرده اید پیدا کنید


HttpCookie cookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName];



حال شی ای از FormsAuthenticationTicket را بازیابی میکنیم





FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);
FormsIdentity identity = new FormsIdentity(ticket);
string[] roles = ticket.UserData.Split('|');
GenericPrincipal principal = new GenericPrincipal(identity, roles);


Context.User = principal;


*نکات:

1-استفاده از encrypt و decrypt ضریب امنیت کار را بالا می برد

2-همان طور که قبلا گفته شد کلاس FormsAuthenticationTicket به صورت مستقیم از Role پشتیبانی نمی کند و مجبور به استفاده از UserData هستیم.در اینجا ما نقش های مربوط به کاربر را (که از DataBase میخوانیم) را با کاراکتر ‘|’ جدا کردیم و در UserData ذخیره کردیم و حال آنها را بازیابی میکنیم.

ahmad156
سه شنبه 05 شهریور 1392, 17:27 عصر
تنطیمات Web.Config

<authentication mode="Forms">
<forms loginUrl="Login.aspx" name="qazx" timeout="30"/>
</authentication>
<authorization>
<deny users="?"/>
<allow roles="admin"/>


</authorization>


معنای کد بالا:
اگر کاربری تعیین اعتبار نشده به صفحه Login.aspx بفرس. loginUrl="Login.aspx"
نام کوکی را qazx قرار بده. name="qazx"
زمان پیش فرض انقضاء را 30 دقیقه قرار بده. timeout="30"
هیچ کاربر تعیین اعتبار نشده ای حق ورود ندارند. <deny users="?"/>
هیچ کاربر ی حق ورود ندارند.

<deny users="*"/>
کاربرانی که دارای نقش admin هستند مجوز ورود دارند. <allow roles="admin"/>
*توجه:ترکیب 2 عبارت بالا بدین معنی می شود که تنها کاربرانی اجازه ورود دارند که دارای نقش admin باشند
کاربرانی که دارای نقش user و admin هستند مجوز ورود دارند. <allow roles="admin,user"/>



هر کاربری با هر نقشی مجوز ورود دارند. <allow roles="*"/>
همچنین شما میتوانید برای یک پوشه خاص این تنظیمات را داشته باشید.
<location path="Admin">
<system.web>
<authentication>...</ authentication >
<authorization>...</authorization>
</system.web>


</location>


حال شما میتوانید با استفاده از دستور زیر اجازه دسترسی را با توجه به role ها در صفحه ها بررسی کنید


Context.User.IsInRole("admin");


در نظر گرفتن چند نکته:
مرورگر حتما باید از Cookie پشتیبانی کند.در صورتی که پشتیبانی نکند گرچه میتوان با استفاده از URL تعیین اعتبار را تا حدودی انجام داد ولی به خاطر مسائل امنیتی از این کار انصراف میدهیم.
if (!FormsAuthentication.CookiesSupported)

throw new Exception("Cookieless Forms Authentication is not supported for this application.");

aminghaderi
شنبه 10 اسفند 1392, 10:20 صبح
سلام .



در اینجا ما نقش های مربوط به کاربر را (که از DataBase میخوانیم) را با کاراکتر ‘|’ جدا کردیم و در UserData ذخیره کردیم و حال آنها را بازیابی میکنیم.

چطوری از دیتابیس می خونی ؟ این که داره از کوکی رول ها رو بر می داره که؟!



var ticket = new FormsAuthenticationTicket(2, txtUserName02.Text, DateTime.Now, DateTime.MaxValue, true, "admin");

رول ها نباید در کوکی قرار بگیرن چون ریسک امنیتی داره ، مثلا SHA1 الگریتم شکستنش ظاهرا وجود داره (http://www.golubev.com/hashgpu.htm) بعد اون وقت اگه یکی از رول های شما خبر داشته باشه ، اپلیکیشن شما می ره روی هوا! حالا شاید من درست کد شما رو نفهمیدم باشم ؟!

ممنونم.

ahmad156
شنبه 10 اسفند 1392, 17:51 عصر
چطوری از دیتابیس می خونی ؟ این که داره از کوکی رول ها رو بر می داره که؟!

زمانی که کوکی وجود داشته باشد یا همون Remember Me رو انتخاب باشه از کوکی استفاده میکنه در غیر اینصورت از DataBase میخونه.
من اسمی از SHA1 نیاوردم و روش آقای راد رو گفتم استفاده کنن.
فکر نمیکنم role توی cookie مشکل امنیتی پیدا کنه وقتی از روش بالا استفاده بشه

aminghaderi
یک شنبه 11 اسفند 1392, 22:36 عصر
چرا برادر وقتی یه فیشنیگ * روی یوزر ها بزنند و بعد کوکی ها رو بردارند و دی کدش کنند بعد اگر سایت شما یه سایت پر بازدید یا مهم یا سازمانی باشه ، امکان داره کوکی مدیر رو سرقت کنند بعد کاربر (هکر) ثبت نام می کنه می شه مدیریت سایت بعدش هم که مشخصه دیگه.

*یه نوع روش هکه که کوکی کاربر رو از روی سیستمش بر می دارند.

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

راه حل :
فقط رول های رو از پایگاه بخونیم هر بار ، برای این کار هم دات نت کلاس هایی مثل شی Dataset داره که داده ها رو فچ می کنه و داخل رم نگه می دارم ، کاربران مدیر که n نفر نیستند ، محدودند ، نهایتا 1000 تا یوزر رو توی رم نگه می داریم و هر بار ار روز اون مقایسه می کینم ، لیست ها هم برای این کار خوبه .
اگه سایت نقش های ساده تری داشت هم می شه اون های رو توی کوکی گذاشت که فشار روی سرور نیاد.
خیلی راه حل می شه پیاده سازی کرد که همین الان یکی دیگه هم تو ذهنم دارم و شما هم حتما راه حل هایی دارین.

ahmad156
چهارشنبه 23 مهر 1393, 12:17 عصر
ببخشید فراموش کردم بگم


من کوکی رو دیکدش می کنم و طریقه کد و دیکدش رو یاد می گیرم ، بعد رول مربوط به مدیر رو می زارم داخل کوکی خودم و دوباره دی کدش می کنم.
به همین سادگی (کارش خیلی تخصصی هست ولی روشش ساده هست)

دوست عزیز Encrypt مربوطه از الگوریتم Rijndael (http://en.wikipedia.org/wiki/Advanced_Encryption_Standard) استفاده میکنه و معمولاً از machineKey به عنوان کلید استفاده میکنه.یعنی عملاً Decrypt کردن اون غیرممکنه!!!