CORS یه قفل مرورگره، نه محافظ سرور
خلاصهٔ کاملتر
نویسنده این مقاله رو با یه خاطره شروع میکنه: تو یه مصاحبه ازش پرسیدن CORS یه سازوکار امنیتی مرورگره یا سرور؟ اون گفت سرور، چون فکر میکرد داریم بکاند رو از دامنههای ناشناس محافظت میکنیم — ولی جوابِ درست مرورگره. به گفتهٔ نویسنده، همین شکاف بین چیزی که خیلیها فکر میکنن و چیزی که واقعا اتفاق میافته، همون چیزیه که ارزش نوشتن داره.
یه تست ساده قضیه رو روشن میکنه: هر اندپوینتی که تو مرورگر خطای CORS میده رو با curl یا Postman صدا بزن، بدون هیچ گلهای دیتا رو برمیگردونه. یعنی سرور کارشو انجام داده و جواب داده؛ فقط مرورگر بود که به مبدأ (Origin) اهمیت میداد. CORS مجموعهای از قانونهاست که مرورگر دربارهٔ خوندنِ جوابهای بیندامنهای رعایت میکنه و فقط وقتی معنی داره که فرانتاند و بکاند رو دامنههای مختلف باشن.
مرورگر به درخواستهای بیندامنهای یه هدر Origin میچسبونه و تو جواب دنبال هدر Access-Control-Allow-Origin میگرده؛ اگه با مبدأ جور باشه جاوااسکریپت اجازه داره جوابو بخونه، وگرنه درخواست به سرور رسیده و اجرا شده ولی مرورگر جوابو قبل از رسیدن به کد دور میندازه. بعضی درخواستها هم یه مرحلهٔ اضافه دارن: اگه متد غیر از GET/POST/HEAD باشه یا هدر سفارشی یا Content-Type مثل application/json داشته باشه، مرورگر اول یه درخواستِ پیشپرواز (preflight) با متد OPTIONS میفرسته و از سرور میپرسه آیا درخواست اصلی مجازه یا نه.
یه نمونه از پیشپروازیه که مرورگر قبل از یه PUT حاوی توکن احراز هویت میفرسته:
OPTIONS /users HTTP/1.1
Origin: https://frontend.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Authorization, Content-Typeسرور تو جوابش با هدرهایی مثل Access-Control-Allow-Methods و Access-Control-Allow-Headers میگه چه متدها و هدرهایی رو قبول میکنه، و با Access-Control-Max-Age مشخص میکنه مرورگر چند وقت نتیجهٔ این پیشپرواز رو کش کنه تا هر بار OPTIONS نفرسته.
حالا CORS دقیقا از چی محافظت میکنه؟ نویسنده مثال میزنه: فرض کن تو یه تب واردِ حساب بانکیت شدی و کوکی نشستت تو مرورگره، بعد تو یه تب دیگه یه سایت مخرب باز میکنی که جاوااسکریپتش با گزینهٔ credentials به bank.com درخواست میده. مرورگر کوکی بانک رو هم میچسبونه، پس درخواست دقیقا شبیه خودت به بانک میرسه و بانک هم جواب و موجودی رو میفرسته؛ ولی چون هدر Access-Control-Allow-Origin با دامنهٔ سایت مخرب جور نیست، مرورگر جوابو به کدِ اون سایت نمیده. پس CORS در واقع کلیدیه که سرور به یه مبدأِ انتخابشده میده تا قفلِ same-origin رو براش باز کنه.
اما نویسنده تأکید میکنه CORS یه دفاع در برابر CSRF نیست. تو حملهٔ CSRF مهاجم اصلا دنبال خوندنِ جواب نیست؛ مثلا یه درخواست POST برای انتقال پول میفرسته که با کوکی معتبر به بانک میرسه و انجام میشه، و CORS فقط جلوی خوندنِ جوابو میگیره که مهاجم اصلا نمیخواستش. این حمله وقتی جواب میده که درخواست «ساده» باشه و پیشپرواز نداشته باشه؛ یه API که فقط JSON قبول میکنه، تقریبا اتفاقی از این در امون میمونه چون مرورگر اول پیشپرواز میفرسته.
دفاعهای واقعی رو باید سمت درخواست گذاشت: کوکی با SameSite=Lax یا Strict که مرورگر اصلا اونو به درخواست بینسایتی نچسبونه، توکن CSRF که مهاجم نتونه بخونه یا حدس بزنه، و بررسی هدر Origin یا Referer سمت سرور. جمعبندی نویسنده اینه که CORS میپرسه آیا جاوااسکریپتِ یه مبدأ دیگه میتونه جوابِ منو بخونه، ولی دفاع CSRF میپرسه آیا اصلا باید این درخواستِ احرازهویتشده رو قبول کنم — دو تا سؤال کاملا جدا.
نکات کلیدی:
- CORS تو مرورگر اجرا میشه، نه سرور؛ سرور همیشه درخواست رو اجرا میکنه و جواب میده
- مرورگر با تطبیقِ هدر Origin و Access-Control-Allow-Origin تصمیم میگیره جاوااسکریپت جوابو بخونه یا نه
- درخواستهای غیرساده اول یه پیشپروازِ OPTIONS دارن
- CORS جلوی CSRF رو نمیگیره؛ برای اون باید از SameSite، توکن CSRF و بررسی Origin/Referer استفاده کرد




