َُASP.NET Session Management -Part1

با سلام و عرض ادب

در این پست میخوام در ارتباط با Session State Management و موارد امنیتی مرتبط با آن مطالبی را بنویسم.

در ابتدا ببینیم که State Management به چه معناست و اصولا در ASP.NET چه راههایی برای حفظ وضعیت(State)  و اطلاعات کاربر وجود دارد.

همانطوریکه میدانید پروتکل HTTP اصطلاحا ،Stateless  میباشد یعنی این پروتکل بنحوی طراحی شده که زمانیکه یک کاربر در حال ارسال درخواستهای متعدد برای یک صفحه یا صفحات متعدد میباشد (مثلا در حال دیدن صفحات مختلف یک فروشگاه اینترنتی میباشد) بطور ذاتی این پروتکل مکانیزمی برای حفظ و نگهداری اطلاعات مرتبط با یک کاربر مشخص را ندارد و به زبان ساده وقتی شما یک صفحه وب را دوبار میبینید (از سرور دوبار درخواست میکنید) سرور هیچ نوع اطلاعاتی از اینکه این صفحه قبلا توسط همین کاربر درخواست شده ندارد. بنابراین برنامه های وب که مبتنی بر این پروتکل هستند باید خودشان این مکانیزم را طراحی و مدیریت کنند که به آن State Management گفته میشود.

در ASP.NET راههای مختلفی برای State Management وجود دارد که به دو دسته Client-Side و Server-Side تقسیم میشوند:

راههای Client-Side شامل:

  • View state  and Control state in ASP.NET WebForms
  • Hidden Fields
  • Cookies
  • QueryString 

راههای Server-Side شامل:

  • Application State
  • Session State
  • Profile
  • ذخیره اطلاعات در Database

برای آشنایی بیشتر با کاربردها و مزایا و معایب هر کدام از این روشها به اینجا مراجعه کنید.

در اینجا بطور خیلی خلاصه روش Session State را مورد بررسی قرار میدهیم.

ASP.NET Session State Management

در ابتدا این مسئله رو بیان کنم که Session State برخلاف تصور بعضی از برنامه نویسان مختص به ASP.NET WebForms نیست و در MVC هم هر چند تاکید بر شیوه برنامه نویسی به شکلی است که Stateless بودن HTTP مورد تاکید قرار بگیرد اما میتوان از آن استفاده نمود.

Session State  به شما این امکان را میدهد که کاربر در زمانیکه درحال بازدید از صفحات مختلف برنامه شما میباشد مقادیر اطلاعاتی مرتبط با این کاربر مشخص را از بین انبوه درخواستهایی که به برنامه میرسد در یک بازه زمانی مشخص حفظ و نگهداری و بازیابی کنید. در ASP.NET کلاس HTTPSessionState    اینکار را بصورت ذخیره اطلاعات بشکل مجموعه ای از  Key-Value انجام میدهد.

برای دسترسی به مقادیر Session از Page.Session یا HttpContext.Session میتوانید استفاده کنید. برای مثال در یک صفحه وب توسط دستور زیر :

Session["TotalItems"]=1;

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

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

ذکر یک نکته ضروری است که منظور از Session بهیچ وجه Logon Session نمیباشد یعنی در اینجا اصلا نیازی نیست که یک کاربر حتما در برنامه لاگین کرده باشد تا بتوان اطلاعات او را ذخیره و ردیابی کرد. برای همین اطلاعاتی که در Session ذخیره میشوند توسط یک ID منحصر بفرد و رندوم به Browser Session مرتبط میشوند  و این ID در داخل یک کوکی با نام پیشفرض ASP.NET_SessionID قرار گرفته (یا در حالتی که Session بصورت Cookieless تنظیم شده باشد ID درURL قرار میگیرد) برای کاربر ارسال میشود. در درخواستهای بعدی کاربر ، کوکی همواره توسط مرورگر برای برنامه ارسال میشود و برنامه توسط ID داخل کوکی که قبلا با اطلاعات Session مرتبط شده قادر است اطلاعات کاربر را بدون اینگه لاگین کند بازیابی کند.

همچنین اطلاعات Session  در ASP.NET  به دو صورت In-Process و یا OutofProcess ذخیره  میشود. بصورت پیشفرض Sessionها InProcess هستند یعنی در داخل فضای حافظه وب سرور (Worker Process) مقادیر مرتبط با کاربران ذخیره میشوند. پر واضح است که اگر شما اطلاعات زیادی از هر کاربر را بخواهید در این حافظه ذخیره کنید براحتی راندمان سرور راپایین آورده و در مواقعی که سایت شما پر ترافیک است سرور دچار مشکل میشود. در حالت OutofProcess میتوان از SQL Server و یا سرویس ASP.NET State Service برای نگهداری اطلاعات کاربران اقدام نمود.

در اینجا ذکر چند نکته ضروری است:

1)  در ASP.NET  تا زمانیکه از SessionState Object استفاده نشود (مقداری در SessionState مانند مثال بالا برای تعداد کالاهای انتخابی کاربر قرار داده نشود ) با هر درخواست از طرف کلاینت یک SessionID جدید به او تخصیص می یابد اما کوکی ارسال نمیشود و فقط زمانیکه مقداری داخل Session Object ذخیره شود یک SessionID برای کلاینت اصطلاحا فیکس میشود و کوکی ASP.NET_SessionID  برای کلاینت توسط Set-Cookie در Response Header  مقداردهی میشود.

اگر فایل Global.asax در پروژه موجود نباشد و یا موجود باشد ولی Event Handler با نام Session_start نداشته باشد با هر درخواست یک SessionID جدید ساخته میشود اما اگر در Global.asax فقط تعریف Session_Start را اضافه کنید و حتی هیچ کدی درون آن نباشد SessionID فیکس میشود.

 2)     Session.IsNewSession  زمانی برابر true میباشد که:

  • اگر Session.Abandon فراخوانی شده باشد و سرور هیچ نوع اطلاعاتی در مورد  Session نداشته باشد. بطور کلی اگر سرور SessionID  را در لیست Sessionها ی خودش نداشته باشد IsNewSession=true میگردد. مثلا زمانیکه کلاینت یک SessionID  دست ساز را برای سرور بفرستد و سرور این ID را در لیست حودش نداشته باشد مقدار True برای IsNewSession در اولین request تنظیم میگردد. اما اگر همین صفحه را Refresh کنیم دیگر IsNewSession=true  نیست. زیرا ASP.NET در پشت صحنه  اگر  Session ID را نداشته باشد به لیست   Cache  خود اضافه میکند و در درخواست دوم دیگر این ID را در لیست دارد و IsNewSession=False میباشد.
  • اولین درخواست جدید به وب سرور فرستاده شده باشد. مثلا زمانیکه Application Pool در IIS را reset کنیم.

 

3)     در ASP.NET  برای داشتن  Session Object معتبر، هیچ لزومی به Authenticate شدن نیست . کوکی Session جدا از کوکی Authentication میباشد و بهیچ وجه به معنای  Logon Session  نمیباشد.

در واقع Sesion State با داشتن ایرادات امنیتی اصلا مکانیزم خوبی برای Logon Session نمیباشد و برای همین ASP.NET از مکانیزم دیگری برای ردیابی و نگهداری اطلاعات کاربرانی که لاگین میکنند استفاده میکند.  در زیر به برخی از دلایل اشاره شده است:

  • SessionID  یک رشته 24 کاراکتری رندوم میباشد که برای اینکار ابتدا توسط متد CreateSessionId از کلاس SessionIDManager یک آرایه 15 بایتی بصورت رندوم تولید میشود و سپس این 120 بیت بصورت 24 کاراکتر Encode میشوند یعنی هر کاراکتر شامل حروف [a-z]  و اعداد [0-5] میباشد که هر کاراکتر 32 حالت (26+6) را دارد بنابراین برای نمایش هر کاراکتر به 5 بیت و مجموعا 120 بیت نیاز میباشد. هیچ نوع Encryption یا Signing بر روی آن اعمال نمیشود.
  • هیچ مکانیزمی برای اعتبارسنجی Session ID در داخل ASP.NET وجود ندارد.(بدین معنی که آیا SessionID توسط Asp.net ایجاد شده یا نه؟). تا زمانیکه Browser قادر به فرستادن SessionID  Well-Formed به ASP.NET میباشد و SessionID دارای فرمت و شکل صحیحی باشد اطلاعات داخل Session  متناظر با  SessionID قابل دسترسی میباشد و اگر ASP.NET اطلاعات Session که با این ID مرتبط باشند را پیدا نکند یک Session Object جدید که با این ID مرتبط میکند را میسازد. ایجاد یک رشته 24 بایتی که فرمت صحیح داشته باشد کار سختی نیست اما حدس زدن یک SessionID که هنوز Expire نشده باشد 24^32 یا به عبارت دیگر 120^2 حالت ممکن دارد که کار راحتی نیست.
  • کوکی Session  بطور پیش فرض HttpOnly میباشد اما درباره HttpOnly نکته مهم اینست با وجود اینکه جاوا اسکریپت نمیتواند کوکی Session را بخواند اما مهاجم همچنان قادر به Set کردن مقدار کوکی به مقدار دلخواه خودش میباشد(تکنیک Cookie Forcing  که در یک پست جداگانه این تکنیک را بررسی خواهیم کرد که با توجه به اینکه این کوکی برخلاف کوکی Authentication رمزنگاری نمیشود یک تهدید امنیتی به حساب می آید.)
  • اطلاعات Session State میتوانند بمدت طولانی حفظ شوند و برخلاف Form Authentication  مکانیزمی بصورت تاریخ انقضا مطلق (Absolute Expiration) برای  اطلاعات  Session نمیتوان تعریف کرد.(مکانیزم بصورت Sliding میباشد)
  • هر Session دارای یک زمان Timeout میباشد که بطور پیشفرض 20 دقیقه میباشد اما ApplicationPool Recycling بر SessionTimeout اثر میگذارد:
  1. زمان Idle Timeout که در IIS برای Shutdown Worker Process Idle Timeoutتنظیم میگردد میبایست حداقل برابر با Session TimeOut باشد(بطور پیش فرض هر دو 20 دقیقه هستند). در غیر اینصورت در سایتهایی که ترافیک سبک میباشد  زمانیکه IdleTimeout فرا برسد appPool Recycle میشود و از عوارض اینکار اینست که Sessionها Terminate میشوند و اطلاعاتی که در Session هستند از بین میروند.  
  2. مقدار حافظه که در Memory Limit و Virtual Memory Limit تنظیم شده مهم میباشد چنانچه برنامه در عرض 5دقیقه به محدودیت حافظه برسد Apppool Recycle میشود.

Application Pool Recycling:

بدین معنی است که (Worker Process (w3wp.exe که در هر Application Pool مسئول پاسخگویی به درخواستهای برنامه میباشد Shutdown شده و یک Worker Process جدید برای اینکار در App pool ایجاد میشود. اینکار برای این انجام میشود که برنامه در یک وضعیت Unstable که منجر به  Crash کردن برنامه میشود قرار نگیرد.

بدلایل فوق از دیدگاه Security بهتر است Session State  به شکل یک فضایی در سرور درنظر گرفته شود که فقط در بعضی موارد برای افزایش Performance اطلاعات داخل آن ذخیره میشود و نه به عنوان Logon Session.

در مورد کوکی Session نکات زیر را مد نظر داشته باشید:

1)  کوکی Session فقط حاوی SessionID میباشد و این کوکی در حافظه Browser میباشد و مرورگر آنرا در زمان فرستادن درخواست به سمت سرور در قسمت Header درخواست میفرستد . در واقع در ASP.NET  کوکی  Session بصورت  Non-Persistent و Non-Expiring بوده و در هارد کلاینت ذخیره نمیشود. بنابراین با بسته شدن پنجره  مرورگر (درحالتی که کلیه tab ها بسته شوند و Process از حافظه سیستم خارج شود ) کوکی نیز از بین میرود  در عین حال سرور هیچ اطلاعی از این امر ندارد و اطلاعات Session معتبر میماند تا زمان Timeout سپری شود (مگر اینکه توسط کلاینت و از طریق مکانیزمهایی مثل Ajax به سرور خبر داده شود).زمانیکه timeout فرا برسد رویداد Session_End در  Global.asax فراخوانی میشود.

2)  در حالتی که مرورگر برای مدت چند ساعت باز باشد و هیچ درخواستی ارسال نشده باشد و تنظیمات sesssion Timeout پیش فرض 20 دقیقه باشد بمحض اینکه اولین درخواست برای سرور بعد از چند ساعت فرستاده شود چون Session منقضی شده است هیچکدام ازاطلاعات Session وجود ندارند اما چون مرورگر باز بوده SessionID همان مقدار قبلی خواهد بود.

3)    در حالتی که چندین برنامه مختلف روی یک سرور قرار داشته باشند (و یا با یک نام DNS در Web Farm بصورت Load-Balanced قرار گرفته باشند) هر زمان کاربری درخواستهایی به این برنامه ها توسط مرورگر ارسال میکند یک و فقط یک  Session Cookie برای مرورگر ارسال میشود و این کوکی بین برنامه ها Share میشود و اگر در یکی از برنامه ها کوکی ازبین برود یا Reset شود(مثلا با فرستادن کوکی Fake) تمامی اطلاعات Session  در کلیه برنامه ها که از کوکی استفاده میکردند از بین خواهد رفت. برای همین است که دستور Session.Abandon  فقط باعث حذف اطلاعات  Session Objectبرای برنامه ای که این دستور را صادر کرده میشود ولی کوکی Session را از بین نمیبرد تا اطلاعات Session دیگر برنامه های روی سرور از بین نروند. زمانیکه Session منقضی میشود نیزهمین اتفاق میفتد و باز هم SessionID از بین نمیرود بلکه بمحض اینکه درخواستی به سرور ارسال شود که کوکی Session آن منقضی شده باشد یک Session Object جدید به SessionID (که از قبل وجود داشته) مرتبط میشود و در واقع منقضی شدن کوکی Session باعث regenerate شدن SessionID نمیشود.(  برعکس Cookie-based Session State  در cookieless Session State بطور پیش فرض با انقضای کوکی یک SessionID جدید تخصیص می یابد.)
 

در پست بعدی در مورد لزوم مرتبط کردن Authentication و کوکی Session و نکات امنیتی مرتبط با این دو پست خواهم زد.

/ 0 نظر / 292 بازدید