X
تبلیغات
آموزش ترفند ها و تکنیک های برنامه نویسی وب و ویندوز

مروارید طلایی

ترفند ها و تکنیکهای برنامه نویسی

امنیت در ASP.NET


در این مطلب قصد داریم شما را با اصول و روش های حفاظت وب سایت های برنامه نویسی شده با تکنولوژی دات نت را در مقابل حملات هکری آشنا کنیم. این مقاله از جنبه های مختلف این موضوع را مورد بررسی قرار داده است. امید است با رعایت این نکات و عدم استفاده از CMS های رایگان جلوی تخریب روزانه وب سایت های ارزشی تا حدودی گرفته شود.

مدیریت اطلاعات مهم:

اطلاعات مهم مثل: نام کاربری، کلمه ی عبور، Connection String، اطلاعات لایسنس و ... هستند.
این مهمه که شما این اطلاعات رو در کجا نگهداری کنید.
مکان نگهداری این اطلاعات اصل ثابتی نیست و بیشتر جنبه ی پیشنهادی داره. معیارهای مختلفی در این انتخاب سهیم هستند.

1) برخی اطلاعات جنبه ی حیاتی دارند و حتی خود برنامه نویس هم بعد از انتشار برنامه نباید از اونها مطلع باشه. مثل کلمه ی عبور حساب بانکی یک فرد. در این حالت این کلمه ی عبور باید با یک الگوریتم Hash به رمز در بیاد و در دیتابیس ذخیره بشه.
الگوریتم های Hash برگشت پذیر نیستند مثل MD5.
بهتر هست که این دسته از اطلاعات با استفاده از یک بستر انتقال امن همانند SSL منتقل بشن.

2) برخی اطلاعات مهم هستند اما نیازی به رمزنگاری ندارند. مثل Connection String.
در این حالت برنامه نویس مطمئن هست که فقط خودش می تونه به این اطلاعات دسترسی داشته باشه نه شخص دیگری.
این اطلاعات بهتره در Web.Config ذخیره بشن.
سوال: در NET 2.0. امکان رمزنگاری (Encryption) محتویات فایل Web.Config وجود داره. آیا پیشنهاد میشه که از این امکان استفاده کنیم؟
پاسخ: نه برای هر نوع اطلاعاتی. به نظر من رمزنگاری Connection String کار بیهوده ای محسوب میشه. هیچ کس به جز شما و هاست به Web.Config دسترسی نداره.
نتیجه گیری اخلاقی: اگر فکر می کنید که احتمال ریختن سقف خونتون وجود داره، هیچ وقت در خونه نمونید! در غیر اینصورت به سقف خونتون اطمینان کنید.
یکی از مزایای مهم قرار دادن Connection String شما در Web.Config، استفاده از قابلیت Connection Pooling هست.
نتیجه گیری: Connection String رو همیشه بدون رمزنگاری در Web.Config قرار بدید.

3) برخی اطلاعات با دیگران به اشتراک گذاشته میشن اما اشتراک اونها شرط خاصی داره.
به عنوان مثال: لایسنس کامپوننتی که باید همراه با اون وجود داشته باشه.
این اطلاعات معمولا در فایلی با پسوند lic در کنار کامپوننت قرار دارند و مثلا می تونند حاوی Key تولید کننده ی سریال نامبر کامپوننت برای بررسی صحت سریال وارد شده توسط خریدار باشند.
این اطلاعات فقط باید توسط برنامه نویس قابل دسترسی باشند نه توسط فردی که اطلاعات با اون به اشتراک گذاشته شده.
این اطلاعات باید توسط یک الگوریتم، Code شوند. مثلا الگوریتم های کد کردن 64 یا 128 بیتی و یا از الگوریتم های متقارن همانند DES برای رمزنگاری اونها استفاده بشه.

نکاتی در باب رمزنگاری:

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

1) الگوریتم Rijndael برای رمزنگاری متقارن خوب هست.
2) الگوریتم 3DES برای رمزنگاری داده هایی که مدت زیادی وجود دارند مناسب هست.
3) الگوریتم های RC2 و DES برای رمزنگاری داده هایی که عمر و ماندگاری کوتاه تری دارند مناسبند.
4) در رمزنگاری های متقارن و نامتقارن همیشه از کلیدهایی با اندازه ی طولانی استفاده کنید. طبق تحقیقاتی که چند وقت پیش شرکت مک آفی منتشر کرد، مشخص شد که شکستن کلیدهای طولانی اما ساده، بیشتر از کلیدهای پیچیده اما کوتاه زمان بر هست!
5) برای Hashing، الگوریتم های MD5 و SHA1 مناسب و کافی هستند.
6) اگر در رمزنگاری نیاز به تولید یک عدد تصادفی داشتید، از کلاس RNGCryptoServiceProvider استفاده کنید. این کلاس کاملا با الگوریتم ها رمزنگاری دات نت منطبق هست و اعداد کاملا تصادفی تولید می کنه. به کلاس Random نمیشه زیاد اعتماد کرد.

استفاده از تصاویر امنیتی در هنگام ورود اطلاعات:

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

محافظت از برنامه در برابر SQL Injection:

در مورد SQL Injection زیاد شنیدید.
به طور خلاصه، به تزریق کدهای SQL از طریق برنامه، SQL Injection گفته میشه.
شاید بشه گفت که خطرناکترین نوع حمله ای که می تونه به یک برنامه صورت بگیره SQL Injection هست که متاسفانه بسیاری از افراد اون رو نادیده میگیرن و نسبت به اون بی توجه هستن.
عدم توجه به توانایی تزریق SQL به برنامه، عملا دیتابیس رو در اختیار فرد مهاجم قرار میده و می تونه تمام هست و نیستی شما رو بر باد بده!
راه جلوگیری از SQL Injection، استفاده از پارامترها برای ارسال مقادیر به دیتابیس هست.
پارامترها می تونن همراه با Stored Procedure استفاده بشن و در صورت عدم استفاده از Stored Procedure ها، با نوشتن مستقیم دستورات SQL در کد برنامه نیز قابل استفاده هستند.
اون چیزی که مهم هست اینه که در هر شرایطی از پارامترها برای پاس دادن مقادیر ورودی به Command استفاده کنید.

برخی افراد برای مقابله با SQL Injection، برخی کلمات کلیدی SQL، نظیر DELETE، UPDATE، DROP و ... رو در رشته ی ورودی فیلتر می کنند.
این عمل ساده لوحانه، صرف نظر از ایجاد محدودیت برای برخی کلمات در ورودی و Poor Coding، نکته ای رو از دید این افراد دور نگه داشته و اون این هست که:
هر کاراکتر یک کد hexadecimal داره که می تونه به جای کاراکتر اصلی در ورودی قرار بگیره و بدین طریق میشه به راحتی کلمه ی محدود شده رو دور زد!
به عنوان مثال، معادل کلمه ی DROP، عبارت 0x440x520x4F0x50 هست!

از مطلب اخیر سوء استفاده نکنید ;)

پ.ن: از متد HexEscape کلاس Uri می تونید برای به دست آوردن کد HEX کاراکتر استفاده کنید.

مدیریت صحیح خطاها:

1) از روش های مدیریت خطای ساخت یافته (Try Catch Finally) برای هندل خطاهای برنامه استفاده کنید.

2) پس از تشخیص خطا بلافاصله در جهت رفع اون اقدام کنید و این کار رو به تاخیر نیندازید.

3) جزئیات خطا رو در جایی ذخیره کنید. این مکان تنها باید توسط شما قابل دسترسی باشد.

4) هیچگاه جزئیات خطا رو به کاربر نشان ندید. در عوض، پیغام های کلی رو که بیانگر بروز خطاست نمایش بدید.

5) قبل از انتشار برنامه، مقدار خاصیت mode تگ customErrors رو در Web.Config همیشه بر روی On یا RemoteOnly تنظیم کنید تا جزئیات خطاهای هندل نشده به کاربر نشان داده نشن.

6) در بند قبل، حتما صفحه ای رو برای برنامتون ایجاد کنید تا در صورت وقوع یک خطای هندل نشده، کاربر به این صفحه هدایت بشه. این صفحه رو با تنظیم مقدار خاصیت defaultRedirect تگ customErrors به نام این صفحه به برنامه معرفی کنید.

7) نام صفحه ای که موجب وقوع خطا شده در یک Query String با نام aspxerrorpath قرار می گیره و به صفحه ای که در بند قبل اشاره شد پاس داده میشه. از طریق بازیابی مقدار این Query String می تونید متوجه بشید که کدام صفحه موجب بروز خطا شده.

8) برخی خطاها ارتباطی با کدهای برنامه ندارند و یک عامل خارجی باعث رخ دادن اونها میشه. مثل تایپ نام صفحه ای که وجود نداره در Address bar توسط کاربر که باعث نمایش پیغام Page Not Found (کد 404) در مرورگر میشه. این نوع خطاها رو میشه به دو طریق هندل کرد.
یا از طریق زیر تگ error که یکی از زیر تگ های customErrors در Web.Config هست و یا از طریق هندل کردن روال Application_Error در فایل Global.asax.

برای هندل کردن خطا در روال Application_Error نیز متد GetLastError کلاس Server رو فراخوانی کنید.
نکته ی مهم: در این حالت، در آخرین خط این روال، متد ClearError کلاس Server رو حتما فراخوانی کنید تا Stack از خطا خالی بشه و خطا مجددا رخ نده.

9) قبل از انتشار برنامه، trace رو با تنظیم خاصیت enabled تگ trace در Web.Config به false غیر فعال کنید.

امنیت ViewState:

ViewState مفهومی در برنامه های ASP.NET برای حفظ وضعیت کنترل های صفحه در هنگام PostBack هست.
ViewState می تونه توسط فرد مهاجم دستکاری بشه و داده های ارسال شده رو مثلا مستعد حملات XSS کنه. (در مورد XSS به زودی می نویسم)
ASP.NET قابلیتی رو با نام Message Authentication Check که به اختصار MAC نامیده میشه فراهم می کنه تا صحت ViewState برای عدم دستکاری شدن اون بررسی بشه. (MAC گاهی اوقات مخفف Machine Authentication Check نیز در نظر گرفته میشه)
EnableViewStateMac به طور پیش فرض فعال هست.
وضعیت کنترل های صفحه به صورت یک مقدار رشته ای که با یک الگوریتم رمزنگاری 64 بیتی کد شده همراه با صفحه به کلاینت ارسال میشه. این مقدار در یک فیلد مخفی با نام VIEWSTATE__ قرار می گیره که در صورتی که Source صفحه رو در مرورگر ببینید، این مقدار مشهود هست.
در صورتی که EnableViewStateMac فعال باشه، بر روی ViewState الگوریتم SHA1 اعمال میشه (کلید این الگوریتم Hash در تگ فایل machine.config تعریف شده و توسط LSA به طور خودکار تولید میشه) و یک مقدار Hash شده به دست میاد که این کد قبل از ارسال ViewState به کلاینت به انتهای اون اضافه میشه.
این کد پس از PostBack صفحه مجددا بررسی میشه و اگر ViewState توسط فرد مهاجم دستکاری بشه، مسلما مقدار متفاوتی از مقدار اولیه به دست میاد. در این هنگام خطایی رخ میده و ViewState رو Invalid اعلام می کنه.

نتیجه گیری پایانی:
آن چه که مسلم هست اینه که هر عمل رمزنگاری و رمزگشایی به خودیه خود مقداری از زمان رو هدر میده اما اون چیزی که بسیار مهم تر هست، امنیت برنامه ی شماست که این زمان ناچیز در کنار استفاده ی صحیح و بجا از ViewState عامل محسوسی به حساب نمیاد.

محافظت از فایل ها از دسترسی غیر مجاز:

DNN ماژولی برای مدیریت خطاها داره که خطاها رو در یک فایل XML ذخیره می کنه.
در بررسی کدهای DNN مورد جالبی رو دیدم که می تونه ایده ی خوبی برای محافظت از فایل ها از دسترسی غیر مجاز باشه.
وقتی درخواستی به IIS ارسال میشه، اون درخواست تنها در صورتی توسط IIS هندل میشه که جزء پسوندهایی باشه که برای IIS شناخته شده است.
برای دیدن لیست این پسوندها در IIS بر روی Default Web Site راست کلیک و گزینه ی Properties رو انتخاب کنید. در سربرگ Home Directory بر روی دکمه ی Configuration کلیک کنید.
فرض کنید نیاز دارید تا فایل های ZIP رو از دسترسی کاربر غیر مجاز دور نگاه دارید.
دو راه دارید:
1) پوشه ی محتوی فایل های ZIP رو به عنوان پوشه ی Protected از طریق Control Panel معرفی کنید.
2) این کار رو به IIS واگذار کنید.

اگر به روش اول عمل کنید، با محدودیت هایی مواجه میشید. مثلا فرض کنید پرتالی ایجاد کردید و این پرتال رو به مشتری میدید. اگر هاستی که مشتری از اون استفاده می کنه امکان Protected Folders رو فراهم نکنه، با مشکل مواجه میشید. و یا فرضا فایل مورد نظر شما به هر دلیلی باید در ریشه ی اصلی سایت وجود داشته باشه. در این حالت اگر یک Site Map داشته باشید، موتور جستجوگر نمی تونه به Site Map شما دسترسی داشته باشه.

و اما روش دوم:
واگذار کردن وظیفه ی حفاظت از فایل ها به IIS روش خوبی هست اما پسوند فایل باید به IIS معرفی بشه. فایل ZIP جزء پسوندهای شناخته شده توسط IIS نیست و طبیعتا باید این پسوند رو به اون معرفی کرد.
کمتر هاستی این کار رو برای شما انجام میده و اگر هم انجام بده در قبال اون هزینه ای رو دریافت می کنه.
ترفندی که میشه در اینجا استفاده کرد، تغییر پسوند فایل ZIP به یکی از پسوندهایی است که توسط IIS شناخته شده است. DNN پسوند resourcers. رو بدین منظور استفاده کرده تا اگر فردی آدرس مستقیم فایل رو در Address bar وارد کرد، چون پسوند resources. جزء پسوندهایی هست که IIS اون رو هندل می کنه، پس به راحتی میشه دسترسی به این فایل رو کنترل کرد.
اگر خواستید به کاربر اجازه ی دانلود فایل رو بدید، به راحتی با Stream کردن فایل می تونید این کار رو انجام بدید.

محافظت از برنامه در برابر حملات XSS:

حملات XSS یکی از خطرناکترین حملاتی هستند که بسیاری از سایت ها، پرتال ها و فروم های معروف نیز مستعد این نوع حملات هستند.
مستعد بودن برنامه به تزریق کدهای Client Side از طریق ورودی برنامه و عدم تعیین اعتبار ورودی باعث ایجاد حملات XSS میشه.
البته XSS گاهی اوقات با نام CSS نیز شناخته میشه اما به این دلیل که با Cascade Style Sheet اشتباه گرفته نشه، با عنوان XSS شناخته و استفاده میشه.
بیشترین و متداولترین خسارتی که XSS به برنامه وارد می کنه، دزدیدن Cookie کاربر و استفاده از اون به نفع فرد مهاجم هست.
حالتی رو در نظر بگیرید که کاربر فرمی رو پر می کنه و اون رو ارسال می کنه. اگر در فیلدهای این فرم به جای اطلاعات مرسوم، کدهای Client Side وارد شده باشند، کاربر به راحتی در معرض آسیب پذیری خطرناکی قرار می گیره.
فرض کنید من به عنوان فرد مهاجم، عبارت اسکریپت نمایش کوکی کاربر رو وارد کنم.
این عبارت به عنوان یک مقدار ذخیره میشه. همچنین یک کوکی تصدیق هویت یا یک کوکی که حاوی اطلاعات مهمی (مثلا شماره حساب بانکی) از کاربر هست بر روی سیستم کاربر ذخیره کردیم.
در ادامه فرض کنید که قسمتی رو برای نمایش اطلاعات وارد شده به کاربر در نظر گرفتید. کاربر در زمان لود صفحه، شماره حساب خودش رو می بینه!
من می تونم ورودی رو کمی تغییر بدم تا این اطلاعات برای من ارسال بشه. چون در اینجا قصد بر آموزش هک نیست وارد جزئیات نمیشم.

توجه داشته باشید که مقصود از ورودی هر جایی در برنامه ی شماست که اطلاعات از اون طریق به سرور ارسال میشه. این مکان می تونه Query String و هدرهای HTTP نیز باشه.

راه های مختلفی برای مقابله با حملات XSS وجود داره.

اولین و مهم ترین گام، تعیین اعتبار مقادیر ورودی هست.
بهترین راه برای تعیین اعتبار، استفاده از Reqular Expressions یا همان عبارات باقاعده هست.
دومین گام، کد کردن مقادیر ورودی هست.
برای کد کردن مقادیر ورودی میشه از متد HtmlEncode کلاس HttpUtility استفاده کرد.
برای کد کردن URL از متد UrlEncode کلاس HttpUtility استفاده کنید.

نکته 1:
در IE 6.0 SP1 خصوصیتی با نام HttpOnly برای کوکی ها در نظر گرفته شد که در صورتی که یک کوکی این خصوصیت رو داشته باشه، اجازه ی دسترسی به اون به کدهای Client Side داده نمیشه.
این خاصیت رو می تونید از طریق تنظیم خاصیت HttpOnly کلاس HttpCookie تنظیم کنید.
خوشبختانه پشتیبانی از این خصوصیت از نسخه ی 2.0.0.5 در Firefox نیز لحاظ شده اما باگی در Firefox وجود داره که با استفاده از هسته ی اصلی AJAX که همون XMLHTTPRequest هست میشه به کوکی دسترسی داشت.
این باگ در IE 7.0 برطرف شده.
جزئیات بیشتر در مورد این باگ رو در لینک بخونید: http://ha.ckers.org/blog/20070719/firefox-implements-httponly-and-is-vulnerable-to-xmlhttprequest

کته 2: ابزارهایی همچون Anti-Cross Site Scripting Library توسط مایکروسافت به منظور مقابله با XSS در ASP.NET برای برنامه نویسان عرضه شده.

نتیجه گیری پایانی: وجود خصوصیت HttpOnly که البته خوب هست دلیلی بر این نیست که نسبت به حملات XSS بی توجه باشید.

محافظت از برنامه در برابر Session Hijacking:

یکی از قدیمی ترین حملاتی که به برنامه های وب صورت می گیره، Session Hijacking (سرقت Session) هست.
برای آشنایی با این نوع حمله بهتره که کمی در مورد نحوه ی هندل کردن Session ها در ASP.NET صحبت کنیم.
Session برای نگهداری داده هایی به کار میره که برای هر کاربر منحصر به فرد هستند. مثلا نمایش نام کاربر پس از لوگین، در تمامی صفحات سایت.
Session در حافظه ی سرور نگهداری میشه. ASP.NET برای اینکه بتونه تشخیص بده که هر Session متعلق به کدام کاربر هست، مشخصه ای یکتا برای هر Session ایجاد می کنه و این مشخصه رو در یک کوکی با نام ASP.NET_SessionID قرار میده. با ایجاد هر درخواست به سرور، ASP.NET مشخصه ی موجود در این کوکی رو بررسی می کنه و از این طریق متوجه میشه که کدام Session در حافظه ی سرور متعلق به کاربر هست.
به این مشخصه ی یکتا Session ID گفته میشه.
پر واضح هست که اگر کوکی ها در مرورگر کاربر غیر فعال باشند، استفاده از Session عملا کاربردی نداره.

به طور خلاصه اگر فردی بتونه این کوکی رو به سرقت ببره، به راحتی می تونه خودش رو صاحب Session شما معرفی کنه.
میزان خطری که Session Hijacking در پی داره به نوع اطلاعاتی که در اون هست و نحوه ی استفاده ی برنامه نویس از Session بستگی داره.
به عنوان مثال، در حالتی که از Session تنها برای نگهداری نام کاربری استفاده میشه، سرقت Session سودی برای فرد مهاجم نداره چون تنها چیزی که ممکنه نصیب اون بشه (که البته باز هم بستگی به نحوه ی برخورد برنامه نویس با Session داره) نام کاربری هست!
اما اگر مثلا شماره ی حساب بانکی یا سطح دسترسی کاربر در Session ذخیره میشه با خطری جدی مواجه هستید.

Session Hijacking معمولا به دو طریق انجام می گیره:
1) حدس زدن Session ID
2) سرقت کوکی حاوی Session ID

حدس زدن Session ID
این مورد به ندرت اتفاق می افته چون ASP.NET به طور خودکار این ID رو تولید می کنه و اعدادی که تولید میشن 120 بیتی و کاملا راندوم هستند. البته میشه تولید این ID ها رو خودتون با استفاده از کلاس SessionIDManager بر عهده بگیرید اما این کار اصلا پیشنهاد نمیشه. فرضا اگر روال شما برای اختصاص SessionID یک عدد تصاعدی مثل فیلدهای از نوع AutoNumber در SQL Sever باشه، فرد مهاجم می تونه با بررسی چند باره ی محتویات کوکی ذکر شده، از این روش مطلع بشه.

سرقت کوکی حاوی Session ID
در مورد سرقت کوکی در مقاله ی قبلی توضیح دادم. XSS می تونه یکی از روش هایی باشه که منجر به سرقت کوکی ها میشه.

راهکار مقابله به Session Hijacking
راهکاری که میشه استفاده کرد، "منحصر به فرد تَر کردن" Session ID هست.
بدین شکل که برخی مشخصه های کاربر همانند IP و User Agent رو همراه با یک کلید رمز و خود SessionID به یک الگوریتم Hash همانند HMACSHA1 که یک کلید رو برای رمزنگاری می پذیره داد و رشته ی رمزنگاری شده رو به انتهای SessionID اضافه کرد.
فرایند فوق رو به راحتی میشه از طریق یک HttpModule پیاده سازی کرد.
روال EndRequest مکان خوبی برای الصاق مشخصه ی به دست آمده به انتهای SessionID هست.
روال BeginRequest نیز می تونه برای بررسی صحت این مشخصه استفاده بشه.

کدنویسی بر عهده ی خودتون ;)

نتیجه گیری پایانی: هر چند که هیچ روشی همواره امنیت رو به طور صد در صد تضمین نمی کنه، اما امنیت رو باید یک وضعیت نسبی در نظر گرفت. مجموعه تلاش هایی که انجام میدیم تا راه نفوذ به برنامه رو برای فرد مهاجم سخت تر کنیم.

منبع: سایت vjihad.ir

برگرفته از سایت برنامه نویس

 نوشته شده در    توسط   | 

 
تحلیل آمار سایت و وبلاگ