امروزه یکی از بزرگترین دغدغههای فعالان حوزه آی تی، برقراری امنیت اطلاعات میباشد. با پدید آمدن بانکهای دادهای آماری و مالی، حساسیت مسئله صد چندان میشود. در ادامه چک لیستی را ارائه مینمایم که با کمک آن میتوانید تا حدود بسیار خوبی امنیت نرم افزار تحت وب خود را برقرار نمایید. در برخی از موارد مثالهایی از تکنولوژی مایکروسافت آورده شده است که این بدلیل تخصص نویسنده در تکنولوژیهای مایکروسافت میباشد. در صورتیکه شما از تکنولوژیها و زبانهای سورس باز بهره میبرید، میبایست معادل مورد ذکر شده را در زبان مورد استفاده خود بیابید .
ابتدا اجازه دهید مقداری با حملات آشنا شویم و سپس راه مقابله را در کنار هم بررسی نماییم.
مهمترین و خطرناکترین حملات سطح وب :
حمله 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
برای مثال لینکهای http://www.site.com و http://www.site.com/index.htm دو هش متفاوت را تولید میکنند اما در عمل یکی هستند. نمونهی دیگر، لینکهای http://www.site.com/index.htm و http://www.site.com/index.htm#section1 هستند که فقط اصطلاحا در یک fragment با هم تفاوت دارند و از این دست لینکهایی که باید در حین ثبت یکی درنظر گرفته شوند، زیاد هستند و اگر علاقمند به مرور آنها هستید، میتوانید به صفحهی URL Normalization در ویکیپدیا مراجعه کنید.
اگر نکات این صفحه را تبدیل به یک کلاس کمکی کنیم، به کلاس ذیل خواهیم رسید:
using System; using System.Web; namespace OPMLCleaner { public static class UrlNormalization { public static bool AreTheSameUrls(this string url1, string url2) { url1 = url1.NormalizeUrl(); url2 = url2.NormalizeUrl(); return url1.Equals(url2); } public static bool AreTheSameUrls(this Uri uri1, Uri uri2) { var url1 = uri1.NormalizeUrl(); var url2 = uri2.NormalizeUrl(); return url1.Equals(url2); } public static string[] DefaultDirectoryIndexes = new[] { "default.asp", "default.aspx", "index.htm", "index.html", "index.php" }; public static string NormalizeUrl(this Uri uri) { var url = urlToLower(uri); url = limitProtocols(url); url = removeDefaultDirectoryIndexes(url); url = removeTheFragment(url); url = removeDuplicateSlashes(url); url = addWww(url); url = removeFeedburnerPart(url); return removeTrailingSlashAndEmptyQuery(url); } public static string NormalizeUrl(this string url) { return NormalizeUrl(new Uri(url)); } private static string removeFeedburnerPart(string url) { var idx = url.IndexOf("utm_source=", StringComparison.Ordinal); return idx == -1 ? url : url.Substring(0, idx - 1); } private static string addWww(string url) { if (new Uri(url).Host.Split('.').Length == 2 && !url.Contains("://www.")) { return url.Replace("://", "://www."); } return url; } private static string removeDuplicateSlashes(string url) { var path = new Uri(url).AbsolutePath; return path.Contains("//") ? url.Replace(path, path.Replace("//", "/")) : url; } private static string limitProtocols(string url) { return new Uri(url).Scheme == "https" ? url.Replace("https://", "http://") : url; } private static string removeTheFragment(string url) { var fragment = new Uri(url).Fragment; return string.IsNullOrWhiteSpace(fragment) ? url : url.Replace(fragment, string.Empty); } private static string urlToLower(Uri uri) { return HttpUtility.UrlDecode(uri.AbsoluteUri.ToLowerInvariant()); } private static string removeTrailingSlashAndEmptyQuery(string url) { return url .TrimEnd(new[] { '?' }) .TrimEnd(new[] { '/' }); } private static string removeDefaultDirectoryIndexes(string url) { foreach (var index in DefaultDirectoryIndexes) { if (url.EndsWith(index)) { url = url.TrimEnd(index.ToCharArray()); break; } } return url; } } }
برای مثال اگر یک فایل OPML چنین ساختار XML ایی را داشته باشد:
<?xml version="1.0" encoding="utf-8"?> <opml xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" version="1.0"> <body> <outline text="آی تی ایرانی"> <outline type="rss" text="فید کلی آخرین نظرات، مطالب، اشتراکها و پروژههای .NET Tips" title="فید کلی آخرین نظرات، مطالب، اشتراکها و پروژههای .NET Tips" xmlUrl="https://www.dntips.ir/Feed/LatestChanges" htmlUrl="https://www.dntips.ir/" /> </outline> </body> </opml>
using System.Xml.Serialization; namespace OPMLCleaner { [XmlType(TypeName="outline")] public class Opml { [XmlAttribute(AttributeName="text")] public string Text { get; set; } [XmlAttribute(AttributeName = "title")] public string Title { get; set; } [XmlAttribute(AttributeName = "type")] public string Type { get; set; } [XmlAttribute(AttributeName = "xmlUrl")] public string XmlUrl { get; set; } [XmlAttribute(AttributeName = "htmlUrl")] public string HtmlUrl { get; set; } } }
var document = XDocument.Load("it-92-03-01.opml"); var results = (from node in document.Descendants("outline") where node.Attribute("htmlUrl") != null && node.Parent.Attribute("text") != null && node.Parent.Attribute("text").Value == "آی تی ایرانی" select new Opml { HtmlUrl = (string)node.Attribute("htmlUrl"), Text = (string)node.Attribute("text"), Title = (string)node.Attribute("title"), Type = (string)node.Attribute("type"), XmlUrl = (string)node.Attribute("xmlUrl") }).ToList();
using System.Collections.Generic; namespace OPMLCleaner { public class OpmlCompare : EqualityComparer<Opml> { public override bool Equals(Opml x, Opml y) { return UrlNormalization.AreTheSameUrls(x.HtmlUrl, y.HtmlUrl); } public override int GetHashCode(Opml obj) { return obj.HtmlUrl.GetHashCode(); } } }
var distinctResults = results.Distinct(new OpmlCompare()).ToList();
جلسه اول:
اولین قدم در تولید و توسعه نرم افزار داشتن یک نگرش سیستمی به بسته یا محصول نرم افزاری میباشد. اما چرا ما باید نرم افزار را به عنوان یک سیستم در نظربگیریم ؟
جواب این سئوال را باید از تعریف تئوری سیستم و خصوصیاتی که یک سیستم دارا میباشد استخراج کنیم.
تئوری سیستمها
دانشی برای سهولت کار با سیستمها و بررسی دقیق این مفهوم است ؛ در واقع تئوری سیستمها روشی برای شناخت محیط اطراف یا روشی برای شناخت دنیای واقع میباشد .
از تعریف فوق میتوان نتیجه گرفت :
برنامه نویسان برای ساخت برنامه هایی که با نیاز کاربران همسو باشد ، نیاز به شناخت محیطی دارند که کاربران در آن فعالیت میکنند پس برای شناخت محیط باید با دید سیستمی به مسئله نگاه کرد.
خصوصیات مهم سیستم :
1. محیط – Environment: هر سیستم در یک محیط قرار دارد.
2. مرز – Boundary : سیستمهای موجود در یک محیط توسط مرزها از یکدیگر جدا میشوند.
3. ورودی و خروجی – I/O : هر سیستم ورودی هایی را از محیط میگیرد و خروجی هایی را به محیط پس میدهد.
4. واسط – Interface : امکان محاوره سیستمها در یک محیط را فراهم میکند.
5. زیر سیستم – Sub System : هر سیستم میتواند حاوی چندین زیرسیستم باشد . زیر سیستمها تمام خصوصیتهای یک سیستم را دارا میباشند.
6. مکانیزم کنترلی – Controller : مهمترین بخش یک سیستم میباشد. مکانیزم کنترلی در واقع کنترل کننده تمامی فعالیتهای انجام شده توسط یک سیستم است . ورودیها از طریق مکانیزم کنترلی دریافت میشود و بر اساس آن خروجی هایی به محیط پس داده میشود.
نتیجه گیری :
با توجه به خصوصیاتی که در مورد سیستمها مطرح شد به راحتی میتوانیم دلیل علاقه مندی برنامه -نویسان به نوع نگرش سیستمی را در یابیم ، و جود محیط پیرامون یک سیستم و نحوه تبادل اطلاعات این سیستم با سایر سیستمها در این محیط ، شکستن یک سیستم به چند زیر سیستم برای راحتی مسئله و پیاده سازی آسانتر آن و نیز وجود اینترفیسها برای برقراری محاوره ای استاندارد بین زیر سیستمهای یک سیستم و همچنین وجود ورودی هاو تصمیم گیری براساس ورودی هاو تولید یک خروجی همه و همه از نکات مورد توجه برنامه نویسان در تولید یک بسته نرم افزاری هستند که هماهنگی کاملی با مفاهیم تئوری سیستمها دارند.
با توجه به فاکتورهای موجود در Defensive Coding، یکی از مواردی که کیفیت کد شما را تضمین خواهد کرد، استفاده کردن از (ACT) Automated Code Test میباشد. در این قسمت قصد داریم مزایای تست اتوماتیک و Unit Test را به عنوان یکی دیگر از ابعاد Defensive Coding ذکر کنیم.
به عنوان برنامه نویسی که در حال توسعه کد هستید و قابلیتهای جدیدی را به کد خود اضافه میکنید، باید کدی را که در آن تغییر ایجاد میکنید، مرتب تست کنید، تا بررسی کنید که آیا به هدف خود رسیدهاید و توانستهاید قابلیت جدیدی را در نرم افزار اضافه کنید. روشی که توسط اکثر برنامه نویسان انجام میشود به این صورت است که بهصورت متوالی برنامه را اجرا میکنند، اجرا میکنند، اجرا میکنند و باز هم اجرا میکنند، تا اینکه مطمئن شوند همه چیز درست است. بیشتر زمانیکه در این فرآیند صرف میشود، صرف کارهای تکراری میشود. این مسئله بدون شک برای بسیار از برنامه نویسان پیش آمده است و ممکن است بعضی از این برنامه نویسان به دنبال راه چارهای بوده باشند و بعضیها هم با انجام دادن این روش تست، هیچ مشکلی نداشته باشند. اما مسئلهای که غیر قابل چشم پوشی است، این است که این روش انرژی و زمان بسیار زیادی را از برنامه نویسان میگیرد؛ راه حل چیست؟ راه حل همان ACT میباشد.
ACT به معنی نوشتن کد، جهت تست قابلیتهای نرم افزار میباشد. به این معنی که شما جهت تست کد خود، یکسری کد مینویسید که این کدها وظیفه دارند کدهای جدیدی را که به نرم افزار خود اضافه کرده اید، تست کنند و اجرای آنها توسط زیرساختهای موجود (Test Frameworks) به صورت اتوماتیک انجام میشود.
حال قصد داریم اجزای ACT را که در شکل ذیل نمایش داده شدهاند، تشریح کنیم.
· structured: برای بیان این مسئله، از مفهوم AAA استفاده میشود. A اول به معنی Arrange اطلاعاتی است که برای تست مورد نیاز است. A دوم به معنی Act یا اجرای متد در حالت تست است و A سوم بمعنی Assert یا بررسی نتایج تست میباشد. این ساختار، ساختاری است که در ادامه برای ایجاد تستها از آن استفاده میکنیم.
· Self-documented: ساختار تست به گونهای است که خود مستند میباشد و با بررسی کلی ساختار آن میتوان به هدف تست پی برد.
· Automatic: با استفاده از Test Framework ها، فرآیند تست اتوماتیک میشود.
· Repeatable: یکی از مزیتهای ACT این است که میتوان آن را برای دفعات مکرر تکرار کرد.
· TARDIS: مخفف Time And Relative Dimension In Space میباشد؛ با توجه به این مسئله ACT از کد شما در میان زمان و فضا محافظت میکند. ACT عملکرد اصلی کد شما را در حال حاضر و در زمانی در آینده تایید میکند؛ زمانیکه کد شما در حال توسعه میباشید و هر لحظه قابلیتهای جدیدی به آن اضافه میشود، ACT تضمین میکند که این تغییرات، قابلیتهای موجود در سیستم را تحت تاثیر قرار نمیدهند. بنابراین ACT از کد شما در مقابل زمان و فضا محافظت میکند.
روشهای مختلفی برای انجام دادن ACT وجود دارند که در این مقاله بر روی Unit Test تمرکز خواهیم کرد. Unit Test یکسری تستها هستند که توسط برنامه نویس نوشته و اجرا میشود. هدف این روش این است که کد به قسمتهای کوچکی تقسیم شود و بررسی شود که این قسمتها آن گونه که انتظار میرود، عمل میکنند.
برای رسیدن به این هدف باید کد را به صورت متدهای Clean و Testable نوشت. این متدها قسمتهای مستقلی از کد هستند که میتوانند تست شوند. همان طور که در شکل زیر مشاهده میکنید، برای هر متد میتوان تستهای مختلفی نوشت و حالتهای مختلف مربوط به ورودیهای معتبر، ورودیهای نامعتبر و بروز Exception را تست کرد.
بسیاری از برنامه نویسان و مدیران پروژه درمقابل مسئله استفاده از Unit Test در توسعه نرم افزار حساسیتهای خاصی نشان میدهند. بسیاری از آنها اظهار میکنند که برای این کار زمان کافی نداریم و استفاده کردن از این روش برای ما هزینه بر میباشد. اما ما در جواب این دسته از افراد باید موارد زیر را که بیشتر هم بر جنبه زمانی تاکید دارند، بیان کنیم.
· Save time:
استفاده کردن از Unit Test از هدر رفتن زمان شما جلوگیری میکند. هر برنامه نویسی میداند که حتی چند خط کد ساده هم نیاز به تست و باز بینی دارد. بنابراین برنامه نویس مجبور است آن ماژول اصلی از نرم افزار را که چند خط کد در آن نوشته است، به گونه ای اجرا کرده و فرآیند بیزینسی این ماژول را برای سناریوهای مختلف، بصورت دستی تست کند. حال فرض کنید در ادامهی این کار، شخص برنامه نویس مجبور شود کد را بطور مرتب تغییر دهد. بنابراین در این حالت مجبور است این فرآیند را چندین و چند بار تکرار کند (نرم افزار را اجرا کند، به منوی X برود، فرم Y را باز کند، حالتهای مختلف را در فرم بررسی کند).
راه حلی که Unit Test برای حل این مشکل ارئه میدهد این است که برای انجام این فرآیند میتوان کد نوشت و آن را بارها و بارها اجرا کرد. وظیفهی Unit Test ها این است که اطلاعات مورد نیاز متد یا واحدی که میخواهند آن را تست کنند، فرآهم میآورند، متد را با اطلاعات فرآهم شده زیر تست میبرند و سپس نتایج بدست آمده را بررسی خواهند کرد. شما میتوانید در صورت تغییراتی در متدها یا واحدها، Unit Test را بارها و بارها برای تست عملکرد صحیح آن متد، بعد از تغییرات اجرا کنید. همان طور که میبینید تبدیل کردن این فرآیند دستی به یک فرآیند سیستمی و اتوماتیک میتواند در جلوگیری از هدر رفت زمان بسیار تاثیر گذار باشد.
· Find Bugs Faster:
با استفاده از Unit Test شما میتوانید فرآیند پیدا کردن خطاها را بسیار سریعتر انجام دهید. برای مثال فرض کنید که شما گزارش یکسری خطاها را در نرم افزار، دریافت کردهاید. به جای اینکه سعی کنید بصورت دستی، فرآیندها را در نرم افزار مرور کنید تا دوباره شرایط بروز خطا یا شرایطی را که خطا در آن رخ داده است، جهت درک دلیل خطا یا خطاها ایجاد کنید، با استفاده از Unit Test میتوانید به راحتی و در سریعترین زمان ممکن و بصورت اتوماتیک خطاها را پیدا کنید.
· Refactor Safely:
Unit Test به شما اجازه میدهد که به راحتی کد خود را Refactor کنید. فرض کنید که میخواهید کدی را که دارای یکسری پیچیدگیها میباشد و نگهداری و توسعه آن سخت است، Refactor کنید. بدون استفاده از Unit Test، این Refactor کردن دارای ریسک بسیار زیادی است و ممکن است منجر به بروز خطاهای زیادی شود؛ در حالیکه با استفاده از Unit Test، بعد از Refactor کردن کد، میتوان Test ها را اجرا کرده و از عدم وجود خطا در کدها به راحتی مطمئن شد.
· Enhance Your Value:
با نوشتن Unit Test برای کدهای خود میتوانید یک ارزش افزوده را به کدهای خود اضافه کنید. به دلیل اینکه نوشتن Unit Test ویژگی Self-documented کد شما را افزایش میدهد و به افرادی که در تیم هستند کمک میکنند Business نرم افزار را بهتر درک کنند.
· Minimize Interruptions:
داشتن مجموعهای مناسب از Unit Test ها باعث میشود تا Interrupt های ناخواسته در Code شما بوجود نیاید. برای مثال حالتی را در نظر بگیرید که بدلیل ورود دادههای ناخواسته، نرم افزار دچار خطا میشود. دراین وضعیت در صورتیکه از Unit Test استفاده شود، هندل کردن این شرایط ناخواسته و Interrupt، بسیار راحتتر خواهد بود.
- تمرکز اصلی پروژه، باید صرف فائق آمدن بر مشکلات و پیچیدگیهای موجود در دامین شود.
- پیچیدگیهای موجود در دامین پس از شناسایی به یک مدل تبدیل شوند.
- برقراری یک رابطهی خلاق بین متخصصان دامین و افراد تیم توسعه برای بهبود مستمر مدل ارائه شده که نهایتا راه حل مشکلات دامین است بسیار مهم میباشد.
Eric Evans is a specialist in domain modeling and design in large business systems. Since the early 1990s, he has worked on many projects developing large business systems with objects and has been deeply involved in applying Agile processes on real projects. Eric is the author of "Domain-Driven Design" (Addison-Wesley, 2003) and he leads the consulting group Domain Language, Inc.
- دامین ساده و سر راست نباشد.
- افراد تیم توسعه با طراحی / برنامه نویسی شی گرا آشنا باشند.
- دسترسی به افراد متخصص در مسائل مرتبط با دامین آسان باشد.
- فرآیند تولید نرم افزار، یک فرآیند چابک باشد.
در ادامه میپردازم به اینکه ابزار DDD برای شکستن پیچیدگیهای دامین و تبدیل آنها به مدل کدامند؟
- Entity
- Value Object
- Aggregate
- Service
- Repository
- Factory
قبلا توضیح داده بودم که طراحی متاثر از حوزهی کاری (Domain Driven Design) منجر به کاهش، درک و نهایتا قابل مدیریت کردن پیچیدگیهای موجود در حوزه عملیاتی نرم افزار (Domain) میشود. توجه کنیم که تحلیل و مدلسازی، فعالیتهای رایج در حوزه هایی مانند حمل و نقل، اکتشافات، هوا فضا، شبکههای اجتماعی و ... میتواند بسیار دشوار باشد.
برای مثال فرض کنید که میخواهید به مدلسازی نرم افزاری بپردازید که هدف تولید آن پیش بینی وضع آب و هوا است. این حوزه کاری (پیش بینی وضع آب و هوا) بویژه همواره یکی از حوزه (Domain)های پیچیده برای طراحان مدل محسوب میشود. DDD روشی است که با بکار گیری آن میتوانید این پیچیدگیها را بسیار کاهش دهید. توسعه سیستمهای پیچیده بدون رعایت قواعدی همه فهم، نهایتا نرم افزار را به مجموعه ای از کدهای غیر خوانا و دیرفهم تبدیل میکند که احتمالا نسل بعدی توسعه دهندگانش را تشویق به بازنویسی آن خواهد کرد.
DDD در واقع روشی است برای دقیقتر فکر کردن در مورد نحوه ارتباط و تعامل اجزای مدل با یکدیگر. این سبک طراحی (DDD) به تولید مدلی از اشیاء میانجامد که نهایتا تصویری قابل درک (Model) از مسائل مطرح در حوزهی کاری ارائه میدهند. این مدل ارزشمند است وقتی که:
1- نمایی آشکار و در عین حال در نهایت سادگی و وضوح از همهی مفاهیمی باشد که در حوزه عملیاتی نرم افزار(Domain) وجود دارد.
2- به تناسب درک کاملتری که از حوزه کاری کسب میشود بتوان این مدل را بهبود و توسعه داد(Refactoring).
در DDD کوشش میشود که با برقراری ارتباطی منطقی بین اشیاء، و رعایت سطوحی از انتزاع یک مشکل بزرگتر را به مشکلات کوچکتر شکست و سپس به حل این مشکلات کوچکتر پرداخت.
توجه کنیم که DDD به چگونگی سازماندهی اشیاء توجه ویژه ای دارد ولی DDD چیزی درباره برنامه نویسی شی گرا نیست. DDD متدولوژیی است که با بهره گیری از مفاهیم شی گرایی و مجموعه ای از تجارب ممتاز توسعه نرم افزار (Best Practice) پیچیدگیها را و قابلیت توسعه و نگهداری نرم افزار را بهبود میدهد.
ایده مستتر در DDD ساده است. اگر در حوزه کاری مفهوم (Concept) ارزشمندی وجود دارد باید بتوان آن را به وضوح در مدل مشاهده کرد. برای مثال صحیح نیست که یک شرط ارزشمند حوزه کسب و کار را با مجموعهی سخت فهمی از If / Else ها، در مدل نشان داد. این شرط مهم را میتوان با Specification pattern پیاده سازی کرد تا تصویری خواناتر از Domain در مدل بوجود بیاید.
مدلی که دستیبابی به آن در DDD دنبال میشود مدلی است سر راست و گویا با در نظر گرفتن همه جوانب و قواعد حوزهی عمل نرم افزار. در این مدل مطلوب و ایده آل به مسائل ذخیره سازی (persistence) پرداخته نمیشود. (رعایت اصل PI). این مدل نگران چگونگی نمایش دادهها در واسط کاربری نیست. این مدل نگران داستانهای Ajax نیست. این مدل یک مدل Pureاست که دوست داریم حتی المقدور POCO باشد. این مدلی است برای بیان قواعد و منطق موجود در حوزه عمل نرم افزار. این قواعد همیشه تغییر میکنند و مدلی که از آموزههای DDDالهام گرفته باشد، راحتتر پذیرای این تغییرات خواهد بود. به همین دلیل DDD با روشهای توسعه به سبک اجایل نیز قرابت بیشتری دارد.
عموما برای درج فایلهای ثابت اسکریپتها و شیوهنامههای سایت، از روش متداول زیر استفاده میشود:
<link rel="stylesheet" href="/css/site.css" /> <script src="/js/site.js"></script>
مشکلی که به همراه این روش وجود دارد، مطلع سازی کاربران و مرورگر، از تغییرات آنهاست؛ چون این فایلهای ثابت، توسط مرورگرها کش شده و با فشردن دکمههایی مانند Ctrl+F5 و بهروز شدن کش مرورگر، به نگارش جدید، ارتقاء پیدا میکنند. برای رفع این مشکل حداقل دو روش وجود دارد:
الف) هربار نام این فایلها را تغییر دهیم. برای مثال بجای نام قدیمی site.css، از نام جدید site.v.1.1.css استفاده کنیم.
ب) یک کوئری استرینگ متغیر را به نام ثابت این فایلها، اضافه کنیم.
که در این بین، روش دوم متداولتر و معقولتر است. برای این منظور، ASP.NET Core به همراه ویژگی توکاری است به نام asp-append-version که اگر آنرا به تگهای اسکریپت و link اضافه کنیم:
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" /> <script src="~/js/site.js" asp-append-version="true"></script>
این کوئری استرینگ را به صورت خودکار محاسبه کرده و به آدرس فایل درج شده اضافه میکند؛ با خروجیهایی شبیه به مثال زیر:
<link rel="stylesheet" href="/css/site.css?v=AAs5qCYR2ja7e8QIduN1jQ8eMcls-cPxNYUozN3TJE0" /> <script src="/js/site.js?v=NO2z9yI9csNxHrDHIeTBBfyARw3PX_xnFa0bz3RgnE4"></script>
ASP.NET Core در اینجا هش فایلهای یافت شده را با استفاده از الگوریتم SHA256 محاسبه و url encode کرده و به صورت یک کوئری استرینگ، به انتهای آدرس فایلها اضافه میکند. به این ترتیب با تغییر محتوای این فایلها، این هش نیز تغییر میکند و مرورگر بر این اساس، همواره آخرین نگارش ارائه شده را از سرور دریافت خواهد کرد. نتیجهی این محاسبات نیز به صورت خودکار کش میشود و همچنین با استفاده از یک File Watcher در پشت صحنه، تغییرات این فایلها هم بررسی میشوند. یعنی اگر فایلی تغییر کرد، نیازی به ریاستارت برنامه نیست و محاسبات جدید و کش شدن مجدد آنها، به صورت خودکار انجام میشود.
البته این ویژگی هنوز به Blazor اضافه نشدهاست؛ اما امکان استفادهی از زیر ساخت ویژگی asp-append-version با کدنویسی مهیا است که در ادامه با استفاده از آن، کامپوننتی را مخصوص Blazor SSR، تهیه میکنیم.
دسترسی به زیر ساخت محاسباتی ویژگی asp-append-version با کدنویسی
زیرساخت محاسباتی ویژگی asp-append-version، با استفاده از سرویس توکار IFileVersionProvider به صورت زیر قابل دسترسی است:
public static class FileVersionHashProvider { private static readonly string ProcessExecutableModuleVersionId = Assembly.GetEntryAssembly()!.ManifestModule.ModuleVersionId.ToString("N"); public static string GetFileVersionedPath(this HttpContext httpContext, string filePath, string? defaultHash = null) { ArgumentNullException.ThrowIfNull(httpContext); var fileVersionedPath = httpContext.RequestServices.GetRequiredService<IFileVersionProvider>() .AddFileVersionToPath(httpContext.Request.PathBase, filePath); return IsEmbeddedOrNotFound(fileVersionedPath, filePath) ? QueryHelpers.AddQueryString(filePath, new Dictionary<string, string?>(StringComparer.Ordinal) { { "v", defaultHash ?? ProcessExecutableModuleVersionId } }) : fileVersionedPath; } private static bool IsEmbeddedOrNotFound(string fileVersionedPath, string filePath) => string.Equals(fileVersionedPath, filePath, StringComparison.Ordinal); }
در برنامههای Blazor SSR، دسترسی کاملی به HttpContext وجود دارد و همانطور که مشاهده میکنید، این سرویس نیز به اطلاعات آن جهت محاسبهی هش فایل معرفی شدهی به آن، نیاز دارد. در اینجا اگر هش قابل محاسبه نبود، از هش فایل اسمبلی جاری استفاده خواهد شد.
ساخت کامپوننتهایی برای درج خودکار هش فایلهای اسکریپتها
یک نمونه روش استفادهی از متد الحاقی GetFileVersionedPath فوق را در کامپوننت DntFileVersionedJavaScriptSource.razor زیر میتوانید مشاهده کنید:
@if (!string.IsNullOrWhiteSpace(JsFilePath)) { <script src="@HttpContext.GetFileVersionedPath(JsFilePath)" type="text/javascript"></script> } @code{ [CascadingParameter] public HttpContext HttpContext { set; get; } = null!; [Parameter] [EditorRequired] public required string JsFilePath { set; get; } }
با استفاده از HttpContext مهیای در برنامههای Blazor SSR، متد الحاقی GetFileVersionedPath به همراه مسیر فایل js. مدنظر، در صفحه درج میشود.
برای مثال یک نمونه از استفادهی آن، به صورت زیر است:
<DntFileVersionedJavaScriptSource JsFilePath="/lib/quill/dist/quill.js"/>
در نهایت با اینکار، یک چنین خروجی در صفحه درج خواهد شد که با تغییر محتوای فایل quill.js، هش متناظر با آن به صورت خودکار بهروز خواهد شد:
<scriptsrc="/lib/quill/dist/quill.js?v=5q7uUOOlr88Io5YhQk3lgYcoB_P3-5Awq1lf0rRa7-Y" type="text/javascript"></script>
شبیه به همین کار را برای شیوهنامهها هم میتوان تکرار کرد و کدهای آن، تفاوت آنچنانی با کامپوننت فوق ندارند.
Cookie - قسمت اول
مقدمه
GET /index.html HTTP/1.1 Host: www.dotnettips.info |
GET https://www.dntips.ir/ HTTP/1.1 Host: www.dotnettips.info Connection: keep-alive Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17 Accept-Encoding: gzip,deflate,sdch Accept-Language: en-US,en;q=0.8 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3 Cookie: BlogPost-1175=NLOpR%2fgHcUGqPL8dZYv3BDDqgd4xOtiiNxHIp1rD%2bAQ%3d; BlogComment-5002=WlS1iaIsiBnQN1UDD4p%2fHFvuoxC3b8ckbw78mAWXZOSWMpxPlLo65%2bA40%2flFVR54; ReaderEmail=DP%2bx4TEtMT2LyhNQ5QqsArka%2fWALP5LYX8Y
HTTP/1.1 200 OK Content-type: text/html Set-Cookie: cookieName=cookieValue Set-Cookie: cookieName2=cookieValue2; Expires=Thr, 10-Jun-2021 10:18:14 GMT ... |
HTTP/1.1 200 OK Date: Wed, 30 Jan 2013 20:25:15 GMT Server: Microsoft-IIS/6.0 X-Powered-By: ASP.NET X-Compressed-By: HttpCompress Set-Cookie: .ASPXROLES=NzZ9qIRpCWHofryYglbsQFv_SSGPn7ivo0zKFoS94gcODVdIKQAe_IBwuc-TQ-03jGeIkZabTuxA0A3k2-nChy7iAWw9rPMYXSkqzMkizRFkDC0k3gQTkdLqLmmeIfnL9UjfMNWO8iVkYQrSv24ecbpFDSQCH827V2kEj8k2oCm_5sKRSmFpifh4N7kinEi0vomG1vW4Rbg9JWMhCgcvndvsFsXxpj-NiEikC1RqHpiLArIyalEMEN-cIuVtRe7uoo938u9l-7OXb8yzXucVl4bdqPy2DXM3ddWzb3OH1jSFM6gxwJ8qRZDlSGmEEbhji7rA-efI4aYGTKx6heWfUsY6E2k73jJLbuZ3RB4oNwRYmz8FRB0-vm1pO7rhF1JIoi1YB17ez-Ox5chNEFkPVREanHVU9DxboJ5dKgN-2B5udUFPunnshbN8EBhixbFQOpqRiiOK4uWWaWy3rVEJYpCCDBRctKCfEyYD1URFYeajB0AXmiMUTcGeuUtwb-XFjbQZnbylmMF3EJgG16bcc1IEkTAUv1JfKjaql0XGWJI1; path=/; HttpOnly Cache-Control: private Content-Type: text/html; charset=utf-8 Content-Length: 106727 <!DOCTYPE html> <html> ...
GET /index2.html HTTP/1.1 Host: www.example.org Cookie: cookieName=cookieValue; cookieName2=cookieValue2 Accept: */* |
معرفی کتابخانهی DNTScanner.Core