محدودیت امنیتی: سیاست همان-مبدأ
همانطور که قبلاً اشاره شد، یکی از پایههای امنیتی وب، «سیاست مبدأ یکسان» یا Same-Origin Policy
(SOP) است. این سیاست به مرورگر دستور میدهد که به صورت پیشفرض، از ارسال درخواستهای
شبکهای از یک مبدأ (مثلاً https://my-app.com) به یک مبدأ دیگر (مثلاً
https://api.partner.com) توسط اسکریپتها (مانند fetch) جلوگیری کند. این یک مکانیزم
دفاعی حیاتی است که مانع از آن میشود که یک وبسایت مخرب بتواند به اطلاعات خصوصی شما در وبسایت
دیگر (مانند ایمیل یا حساب بانکی شما) دسترسی پیدا کند.
CORS: راه حلی برای عبور از محدودیت
با وجود ضروری بودن SOP، در دنیای مدرن وب که اپلیکیشنها از سرویسها و APIهای مختلف
استفاده میکنند، ما نیاز به راهی برای عبور کنترلشده از این محدودیت داریم. `Cross-Origin
Resource Sharing` یا CORS مکانیزمی است که به سرور اجازه میدهد تا به صورت صریح به مرورگر
اعلام کند که کدام مبدأهای دیگر مجاز به دسترسی به منابع آن هستند.
نکته کلیدی و بسیار مهم این است: CORS یک پیکربندی سمت سرور است، نه سمت
کلاینت. خطاهای مربوط به CORS که در کنسول مرورگر مشاهده میکنید، تقریباً
همیشه ناشی از عدم پیکربندی صحیح سرور هستند و راهحل آنها در کد جاوااسکریپت fetch شما
نیست، بلکه باید توسط تیم بکاند و با تنظیم هدرهای HTTP مناسب روی سرور، رفع شود.
درخواستهای ساده در مقابل درخواستهای نیازمند پیشبازبینی
مرورگر برای مدیریت درخواستهای بین-مبدأ (cross-origin)، آنها را به دو دسته تقسیم میکند.
درخواستهای ساده (Simple Requests)
یک درخواست در صورتی "ساده" تلقی میشود که شرایط خاصی را برآورده کند، از جمله:
- متد آن GET، HEAD یا POST باشد.
- هیچ هدر سفارشی (به جز هدرهای مشخصی) نداشته باشد.
- اگر متد POST است، Content-Type آن یکی از مقادیر
application/x-www-form-urlencoded، multipart/form-data یا text/plain
باشد.
برای این نوع درخواستها، مرورگر درخواست را مستقیماً به سرور ارسال میکند و سپس در پاسخ، به دنبال
هدر Access-Control-Allow-Origin میگردد. اگر این هدر وجود داشته باشد و مقدار آن با مبدأ
صفحه شما مطابقت کند (یا `*` باشد)، مرورگر اجازه دسترسی به پاسخ را به کد جاوااسکریپت شما میدهد.
در غیر این صورت، با وجود اینکه درخواست در تب Network موفق بوده، مرورگر دسترسی به پاسخ را مسدود
کرده و یک خطای CORS نمایش میدهد.
درخواستهای نیازمند پیشبازبینی (Preflighted Requests)
هر درخواستی که شرایط "ساده" را نداشته باشد، نیازمند یک «پیشبازبینی» است. این شامل درخواستهایی
با متدهای PUT، DELETE، PATCH، یا درخواستهایی که هدرهای سفارشی مانند Authorization
دارند، یا یک درخواست POST با Content-Type برابر با application/json میشود.
در این حالت، مرورگر قبل از ارسال درخواست اصلی، یک درخواست مقدماتی با متد OPTIONS به سرور ارسال
میکند تا از آن "اجازه" بگیرد. این درخواست OPTIONS مانند یک بازرسی امنیتی عمل میکند.
فرآیند پیشبازبینی CORS در عمل
کل فرآیند پیشبازبینی به صورت خودکار توسط مرورگر مدیریت میشود و شما نیازی به نوشتن کد برای آن
ندارید. اما درک این فرآیند برای دیباگ کردن خطاهای CORS ضروری است.
۱. ارسال درخواست پیشبازبینی (OPTIONS) از مرورگر
وقتی شما یک درخواست fetch نیازمند پیشبازبینی را اجرا میکنید، مرورگر ابتدا یک درخواست
OPTIONS به همان URL ارسال میکند. این درخواست به سرور میگوید: "من قصد دارم یک درخواست `POST`
با هدرهای Content-Type و Authorization ارسال کنم. آیا مجاز هستم؟"
HTTP REQUEST HEADERS
OPTIONS /api/data HTTP/1.1
Host: api.partner.com
Origin: https://my-app.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization
هدرهای Access-Control-Request-* توسط مرورگر برای پرسیدن اجازه از سرور استفاده میشوند.
۲. پاسخ سرور به درخواست پیشبازبینی
یک سرور که به درستی برای CORS پیکربندی شده باشد، به این درخواست OPTIONS با کد وضعیت
204 No Content و مجموعهای از هدرهای Access-Control-Allow-* پاسخ میدهد. این هدرها
به مرورگر میگویند چه چیزهایی مجاز است.
HTTP RESPONSE HEADERS
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://my-app.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
این پاسخ به مرورگر میگوید که مبدأ https://my-app.com مجاز است، متدهای POST، GET و
OPTIONS مجاز هستند، و هدرهای Content-Type و Authorization نیز مجاز میباشند.
۳. ارسال درخواست اصلی
تنها در صورتی که پاسخ پیشبازبینی موفقیتآمیز و حاوی مجوزهای لازم باشد، مرورگر اقدام به ارسال
درخواست اصلی شما (مثلاً POST) میکند. در غیر این صورت، درخواست اصلی هرگز ارسال نشده و یک خطای
CORS در کنسول نمایش داده میشود.
در این درس با مکانیزم امنیتی CORS و نقش حیاتی آن در ارتباطات تحت وب آشنا شدیم. دیدیم که
CORS یک مشکل سمت کلاینت نیست، بلکه یک سیاست امنیتی است که توسط سرور و از طریق هدرهای HTTP
کنترل میشود و مرورگرها با استفاده از درخواستهای پیشبازبینی OPTIONS آن را اجرا میکنند. درک
این مفهوم برای رفع خطاهای رایج در هنگام کار با APIهای خارجی ضروری است. در درس بعدی، با `Beacon
API` آشنا خواهیم شد که روشی قابل اعتماد برای ارسال حجم کوچکی از دادهها به سرور، درست قبل از
بسته شدن صفحه، فراهم میکند.