نگاره‌هایی پیرامون امنیت، شبکه و رمزنگاری

01 جولای 2020

فرض کنید یک پایگاه‌­داده دارید که شامل اطلاعات مهم هست که اصلا دوست ندارید که فرد دیگه‌ای بهش دسترسی داشته باشه. پس اون رو پیکربندی کردین که فقط از طریق شبکه محلی (Local Network) به اون دسترسی داشته باشید.

چی میشه اگه یک وب­سایت رو باز کنیم و اون وب‌­سایت در کد جاواسکریپت خودش (که حالا روی ماشین شما و با IP شما اجرا میشه) به این سرویس (Database) درخواست بده و مقدار بازگشتی رو برای خودش برگردونه؟

Same-Origin Policy یک سازوکار امنیتی در مرورگرها هستش که ارتباط مستندات (Documents) و Script­ها رو از یک Origin با یک Origin دیگه، محدود میکنه و برای اون­‌ها قانون وضع می­کنه. این سازوکار کمک می­کنه که مستندات آلوده ایزوله باشن و از حملات احتمالی جلوگیری بشه. به این صورت که مرورگر اجازه نمیده که یک Origin محتوایی از Origin دیگه­‌ای بخونه و یا به اون دسترسی داشته باشه.


Origin چیست؟

دو URL رو از یک Origin یکسان می­دونیم وقتی که: پروتکل، پورت (Port)، و نام دامنه (Host) اون­ها یکی باشه. برای روشن شدن موضوع به مثال­ زیر توجه کنید:

این URL رو فرض کنید  http://store.company.com ، حالا قراره بررسی کنیم که کدوم یکی از موارد زیر با این URL در یک Origin یکسان هستند.

در مثال اول و دوم فقط یک مسیر (Directory) متفاوت ارائه شده و هیچ تغییری در پارامترهای مربوط به Origin مشاهده نمی­کنیم. در مثال سوم از https استفاده شده که پروتکل تغییر کرده در نتیجه با URL که اول در نظر گرفتیم، در یک Origin یکسان نیستند. همچنین در مورد چهارم Port استفاده شده 81 است که با توجه به Port پیش ­فرض پروتکل http که 80 است، این دو URL در یک Origin یکسان نیستند. در مورد پنجم هم از یک زیردامنه (Subdomain) متفاوت استفاده شده و در نتیجه Host متفاوتی با URL اولیه داره.


قوانین SOP

شاید براتون سوال شده باشه که چرا وقتی از تگ <img> استفاده می­‌کنید می­تونید محتوای یک تصویر رو از یک URL دیگه که با URL وب­‌سایت شما در یک Origin یکسان نیست فراخوانی کنید؟ خب باید بگم SOP برای تگ <img> قوانین متفاوتی رو داره.

برای مثال ما می­تونیم یک تصویر رو از یک Origin دیگه فراخوانی کنیم و مرورگر شما بدون خطا این کار رو انجام میده:

<img src=”http://statics.example.com/cat.png” alt=”cat” height=”32″ width=”32″>

به صورت «خلاصه»:

  • عملیات نوشتن (Write) بین Origin­های متفاوت معمولا مجاز هست.

برای مثال یک فرم از نوع POST که می­تونه از یک Origin به یک Origin دیگه فرستاده بشه.

  • محتوای تعبیه­‌شده (Embedded Content) بین Originهای مختلف معمولا مجاز هست.

برای مثال:

  • کدهای جاواسکریپ: <script src=”…”></script>
  • کدهای سی اس اس: <link rel=”stylesheet” href=”…”>
  • فایل­ های چندرسانه­ ای: <img>, <video>, <audio>
  • منابع خارجی که توسط تگ <embed>  یا <object> تعبیه شده.
  • فونت­های که توسط @font-face  اضافه شدن (البته در بعضی از مرورگرها این مورد مجاز نیست).
  • هر صفحه ­ای که با تگ <iframe> تعبیه شده باشه.
    •  البته وب­‌سایت‌­ها میتونن با استفاده از هدر X-Frame-Options ، جلوی این تگ رو بگیرن.
  • عملیات خواندن (Read) بین Originهای متفاوت معمولا غیرمجاز هست. هرچند میشه با استفاده از موارد قبلی مثل تگ <img> این کار رو انجام داد.

باز تاکید می­کنم که این موارد به صورت “خلاصه” بیان شده و استفاده از کلمه معمولا به همین خاطر هست. SOP قوانین وسیع­‌تری داره و برای خیلی از شرایط یک سیاست مجزا رو در نظر گرفته. برای مثال وب­‌سایت آلوده نمی­تونه حتی با تگ <img> از ماشین شما تصویری رو فراخوانی  و SOP جلوی این­کار رو می­گیره. و یا فراخوانی یک سرویس رو localhost شما توسط CORB مسدود میشه. و این نکته هم هست که SOP در هر مرورگر و نسخه اون متفاوت هست و دانش در این زمینه اغلب به تجربه بیشتر و مطالعه درباره سازوکار مرورگرها برمی­گرده.


یک مثال عملی دیگه این هست که در وب­سایت YouTube تلاش کنیم تا پیام­های inbox رو از سایت Reddit بگیریم. خب اولین راه ­حل میتونه ایجاد یک XMLHttpRequests باشه:

همون­طور که می­‌بینید بدون هیچ مشکلی تونستیم روی URL مورد نظر GET کنیم. اما یک پاسخ خالی دریافت کردیم. چرا؟ به صورت پیش­فرض یک جلسه (Session) XMLHttpRequests  درخواست ­هارو با اعتبارنامه­ های (Credential)  ذخیره شده نمی‌فرسته و این از نظر SOP مجاز هست. خب الان می­خوام که اعتبارنامه رو همراه درخواستم بفرستم (من در مرورگرم تو Reddit لاگین هستم):

همون­طور که می­بینیم درخواست من توسط CORS مسدود شد.

در مرحله بعدی با تگ <img> شانسمون رو امتحان میکنیم:

خب درخواست­مون با موفقیت ارسال شد و اعتبارنامه ­ها همراه درخواست ما بودن. اما در پاسخ (Response) چیزی نمی­بینیم. چرا؟ خب این­بار CORB به علت این­که MIME Type اطلاعات بازگشتی از نوع application/json بوده محتوا بازگشتی رو مسدود کرده.

یک نکته مهم این هست که Cookieها در SOP ، فقط نام ­دامنه رو چک می­کنن و پروتکل و Port رو نادیده میگیرن. برای مثال هیچ Cookie نمیتونه بین دو Host متفاوت به اشتراک گذاشته بشه (حتی Subdomain). البته در مورد زیردامنه ­ها میشه مقدار domain رو در هدر Set-Cookie در نظر گرفت تا این اجازه داده بشه و زیردامنه ها بتونن از همون Cookie استفاده کنند. برای جزئیات بیشتر در این زمینه به RFC 6265 مراجعه کنید.


خب متوجه شدیم که چرا یک وب­سایت نمی­تونه محتوای دیگه‌­ای رو از روی ماشین شما یا وب سایت دیگه‌ای با Origin متفاوت فراخوانی کنه و به اطلاعات شما دسترسی داشته باشه. اما هنوز یک کد جاواسکریپ آلوده می­تونه یک فرم رو POST کنه و محتوای بازگشتی رو دریافت کنه بدون این­که SOP اون رو مسدود کنه. خب در این موارد چی؟


حملات CSRF

خب تا الان خیلی از چیزهایی که باهم خوندیم موضوع این حمله بودند. حمله CSRF نوعی از حمله در هست که در اون متخاصم (Attacker) سعی میکنه کاربر رو مجبور به اجرای درخواست­‌های ناخواسته بکنه. که این درخواست­‌ها معمولا روی وب‌­سایت‌­هایی هست که کاربر روی اون­‌ها لاگین کرده. برای مثال سناریو زیر رو فرض کنید:

  1. متخاصم یک پیوند (Link) به یک وب‌­سایت آلوده رو به هرصورتی برای کاربر میفرسته.
  2. کاربر با این وب‌­سرویس ارتباط بر قرار می­کنه و با صفحه معمولی از یک وب‌سایت مواجه میشه.
  3. هرچند در این صفحه یک فرم وجود داره که با استفاده از یک کد جاواسکریپت بصورت خودکار اجرا میشه.
  4. و فرض بر اینکه کاربر در وب­سایت slack.com لاگین باشه، یک درخواست vote می­فرسته.

و به همین صورت یه سرویس رای تقلبی ساخته میشه. و همون‌­طوری که در قبل خوندیم یک فرم POST از نظر SOP مجاز هست.


استفاده از علامت­رمزی (Token) برای ارتباط با وب سایت

همون­طور که دیدیم با استفاده از حملات CSRF میشد بدون اطلاع کاربر درخواست POST فرستاده بشه. خب راه­‌حل برای جلوگیری از این اتفاق چیه؟ طبیعتا نمیتونه جلوگیری از POST یک فرم باشه، برای مثال فرض کنید قرار هست از طریق google به یک وب‌سایتی لاگین بشیم و این درخواست بین Originها لازمه.

در این­جا ما از یک علامت‌­رمزی استفاده میکنیم که به اون CSRF Token گفته میشه و کاملا بی­‌معنی هستش. به این صورت که هنگامی که کاربر وارد وب‌­سایت میشه یک Cookie روی مرورگر ثبت میشه که شامل اطلاعات اون نشست (Session) هست. و همینطور در محتوای HTML صفحه یک علامت‌­رمزی ثبت شده. حالا کاربر برای هر درخواست POST، این علامت رو به همراه Cookie که ذخیره شده ارسال می­کنه و وب­‌سرویس ما این دو مقدار رو باهم چک می­کنه تا با سیاست­‌های مورد نظرش منطبق باشه، برای مثال سرور تضمین میکنه که CSRF Token با شناسه نشست (Session ID) که در قالب Cookie فرستاده شده مرتبط باشه و همچنین نشست هنوز منقضی (Expired) نشده باشه.

این سازوکار باعث میشه که فرد سوم که قصد داره بدون اطلاع شما این درخواست POST رو انجام بده نیاز داشته باشه به اون علامت‌­رمزی که تنها با درخواست GET در دسترس هست و عملا بدون اون امکان ارسال یک درخواست معتبر نیست.

به عنوان یک مثال عملی نگاه می­‌کنیم به اطلاعات صفحه ورود وب ­سایت Reddit:

همون­طور که می­بینیم، در Cookie شناسه نشست ثبت شده. همچنین در پایین می­بینیم که یک csrf_token در صفحه HTML برای ما ثبت شده.

حالا یک درخواست Login میفرستیم، و می­بینیم در قالب Data درخواست مقدار csrf_token ارسال شده. و همچنین پاسخ هم میگه نام­ کاربری یا رمزعبورمون اشتباه بوده.

و حالا سعی می­کنیم همین درخواست رو با عوض کردن مقدار csrf_token دوباره ارسال کنیم. این­بار پاسخ متفاوت هست و نشون میده که توکن ارائه شده صحیح نیست و نیاز هست که صفحه رو دوباره GET کنیم. به این صورت از حمله CSRF جلوگیری شد.

هرچند برای عملیات­ های مهم تر مثل حذف اکانت یا جابه­ جایی یه مبلغ، بهتر هست که دوباره از کاربر پسورد خواسته بشه و یا حتی از Captcha استفاده بشه. Captcha هم یکی دیگه از روش­ های جلوگیری از حملات CSRF هست.


پارامتر SameSite در Cookie

هرچند امروزه بیشتر تکنولوژی­ های وب خودشون پیش ­فرض سازوکار جلوگیری از این حمله رو پیاده می­کنن، ولی بد نیست با یکی دیگه از روش ­های جلو گیری از این حمله آشنا بشیم.

پارامتر SameSite یک ویژگی از Cookieهاست که در RFC6265 تعریف شده. این ویژگی به مرورگر میگه که آیا اجازه داره از Cookie ذخیره شده موقع درخواست های بین ­سایتی (Cross-Site) استفاده کنه یا نه. این ویژگی سه مقدار مختلف داره:

  • Strict: این مقدار تضمین میده که مرورگر از Cookie شما برای درخواست های بین‌سایتی استفاده نمی­کنه، هرچند این باعث افت راحتی کابر میشه ولی برای مواردی با سطح امنیتی بالا لازمه.
  • Lax: این مقدار با سیاست­‌هایی که براش تعریف شده عمل می­کنه و در مواقع مورد نیاز مرورگر Cookie رو استفاده می­کنه، این مقدار انتخاب بهتری هست و همزمان ایمنی و راحتی کاربر رو درنظر می­گیره.
  • None: این مقدار هم که در تمام موارد از Cookie استفاده می­کنه.

هرچند این ویژگی قادر به جلوگیری از حملات CRSF هست اما استفاده اون به­ جای Token های اصلا درست نیست. امروزه اغلب مرورگرها از این ویژگی پشتیبانی میکنن ولی در مواقعی ممکن هست که مرورگر از این ویژگی پشتیبانی نکنه و این سازوکار درست عمل نکنه.

برای اطلاعات بیشتر در زمینه جلوگیری از حملات CSRF می­تونید به این لینک مراجعه کنید:

Cross-Site Request Forgery (CSRF) Prevention Cheat Sheet

7 پست نوشته شده
من نه کارشناس امنیت هستم و نه محقق، من حتی یک برنامه‌نویس واقعی هم نیستم. هیچ کدوم از این عنوان‌ها برای من نیست. من فقط علاقه‌مندم به هرچیزی که پشتش یک سازوکار جالب باشه مهم نیس چی باشه من دوست دارم دربارش بخونم.
مطالبی که می نویسم صرفا خلاصه مطالعه RFC ها و کتاب‌های معروفه که همتون می‌شناسید. و هیچ کدوم از این ها کشفیات خودم نبوده و نیست. خوشحال میشم نقد کنید و اگه موردی از نظرتون درست نبود بهم بگید.

راه ارتباطی با من معمولا aggr3ssor@protonmail.com هست و در توییتر با 0xc0d میتونید منو پیدا کنید.
  • به اشتراک بگذارید:
برچسب‌ها: ، ، ، ، ،
  1. […] پست قبلی درباره حملات CSRF خوندیم، این­که چطوری یک وب­سایت آلوده می­تونه کاربر […]

  2. dominate گفت:

    ممنون ، عالی بود

  3. بسیار مقالات خوب و ارزشمندی در اختیار عموم قرار میدین واقعا عالی هستین موفق باشین.