ویرگول
ورودثبت نام
Davood Habbabi
Davood Habbabiاگر وقت کنم چیزایی که یاد گرفتم رو می نویسم
Davood Habbabi
Davood Habbabi
خواندن ۵ دقیقه·۱ ماه پیش

چرا CORS داریم؟

چرا CORS داریم؟
چرا CORS داریم؟

اگه با فرانت‌اند کار کرده باشی، احتمالاً یه روز با یه ارور خیلی اعصاب‌خردکن مواجه شدی:

Blocked by CORS policy

اولش احتمالاً با خودت گفتی: «خب بابا! من فقط می‌خوام یه API رو صدا بزنم، دیگه چرا گیر می‌دی؟!» 😐

ولی واقعاً چرا مرورگر همچین کاری می‌کنه؟

اصلاً CORS چرا به وجود اومده؟

برای جواب دادن به این سؤال، باید اول یه چیز مهم‌تر رو بفهمیم: Same-Origin Policy.

Same-Origin Policy چیه؟

مرورگر از قدیم یه قانون امنیتی مهم داره به اسم Same-Origin Policy یا به اختصار SOP.

ایده‌اش خیلی ساده‌ست:

یه سایت نباید همین‌جوری بتونه به اطلاعات سایت‌های دیگه دسترسی داشته باشه.

فرض کن وارد سایت بانک شدی:

https://bank.com

و توی همون مرورگر هم وارد حساب بانکیت هستی.

حالا یه سایت دیگه رو باز می‌کنی:

https://evil.com

اگر مرورگر هیچ محدودیتی نداشت، سایت evil.com می‌تونست از طرف تو درخواست‌هایی به bank.com بفرسته و شاید به اطلاعات حساس حسابت دسترسی پیدا کنه.

اینجا مرورگر میگه:

«نه عزیزم، همچین خبری نیست!» 😄

و جلوی دسترسی سایت‌ها به منابع Originهای دیگه رو می‌گیره.

Origin اصلاً یعنی چی؟

برای اینکه بفهمیم CORS چطوری کار می‌کنه، باید بدونیم Origin چیه.

Origin از سه بخش تشکیل می‌شه:

  • Protocol

  • Host

  • Port

مثلاً:

https://example.com:443

اینجا داریم:

Protocol → https Host → example.com Port → 443

حالا اینا رو ببین:

https://example.com https://api.example.com http://example.com https://example.com:3000

این‌ها Origin یکسانی ندارن.

مثلاً این دو تا:

https://example.com https://api.example.com

چون Host متفاوت دارن، از نظر مرورگر دو Origin متفاوت محسوب می‌شن.

پس مشکل CORS از کجا شروع میشه؟

فرض کن فرانت‌اندت روی این آدرس اجرا شده:

http://localhost:3000

و بک‌اندت روی این آدرس قرار داره:

http://localhost:8000

تو از فرانت‌اند می‌خوای این درخواست رو بفرستی:

fetch("http://localhost:8000/api/users")

از دید ما که هر دو روی localhost هستن و خیلی عادی به نظر می‌رسن.

ولی مرورگر میگه:

3000 ≠ 8000

پس این دو تا Origin متفاوتن.

اینجاست که CORS وارد ماجرا میشه.

CORS دقیقاً چیکار می‌کنه؟

CORS مخفف این عبارته:

Cross-Origin Resource Sharing

یعنی:

اشتراک‌گذاری منابع بین Originهای مختلف

در واقع CORS یه راه استاندارد به سرور میده که به مرورگر اعلام کنه:

«من اجازه می‌دم فلان Origin به منابع من دسترسی داشته باشه.»

مثلاً سرور می‌تونه در Response این Header رو برگردونه:

Access-Control-Allow-Origin: http://localhost:3000

یعنی:

«به درخواست‌هایی که از localhost:3000 میان اجازه دسترسی بده.»

حالا مرورگر می‌بینه سرور اجازه داده و اجازه می‌ده JavaScript فرانت‌اند Response رو بخونه.

یه نکته خیلی مهم

CORS در اصل قانون سرور نیست؛ رفتار امنیتی مرورگره.

یعنی این تصور اشتباهه که:

«CORS اجازه نمی‌ده Request ارسال بشه.»

در خیلی از درخواست‌ها، مرورگر ممکنه درخواست رو واقعاً به سرور بفرسته، ولی بعد اجازه نده JavaScript صفحه Response رو بخونه.

این تفاوت خیلی مهمه.

مثلاً ممکنه توی DevTools ببینی Request به سرور رفته، ولی مرورگر بعدش بهت بگه:

Access to fetch at ... has been blocked by CORS policy

پس مشکل الزاماً این نیست که Request به سرور نرسیده؛ مشکل می‌تونه این باشه که مرورگر اجازه نداده کد JavaScript به Response دسترسی داشته باشه.

چرا سرور باید اجازه بده؟

چون مرورگر نمی‌تونه برای هر API حدس بزنه که چه کسی اجازه دسترسی داره.

فرض کن یه API داریم:

https://api.example.com/users

سرور می‌تونه بگه:

Access-Control-Allow-Origin: https://mywebsite.com

یعنی:

فقط سایت mywebsite.com اجازه داره Response این API رو از داخل مرورگر بخونه.

ولی اگر سرور این Header رو نفرسته، مرورگر طبق قوانین CORS اجازه دسترسی JavaScript به Response رو نمی‌ده.

Preflight چیه؟

اینجا یکی از قسمت‌های جذاب CORS می‌رسیم: Preflight Request.

بعضی درخواست‌های Cross-Origin قبل از اینکه Request اصلی ارسال بشه، یه درخواست OPTIONS می‌فرستن.

مثلاً مرورگر می‌پرسه:

OPTIONS /api/users Origin: https://mywebsite.com Access-Control-Request-Method: POST Access-Control-Request-Headers: Content-Type

در واقع مرورگر داره از سرور می‌پرسه:

«داداش، من از این Origin میام و می‌خوام با متد POST و این Headerها درخواست بزنم. اوکیه؟»

سرور اگر مشکلی نداشته باشه، مثلاً جواب می‌ده:

Access-Control-Allow-Origin: https://mywebsite.com Access-Control-Allow-Methods: POST Access-Control-Allow-Headers: Content-Type

بعد مرورگر میگه:

«اوکی، بزن بریم.» 😎

و Request اصلی رو ارسال می‌کنه.

چرا همه Requestها Preflight ندارن؟

چون بعضی درخواست‌ها اصطلاحاً Simple Request محسوب می‌شن و نیازی به Preflight ندارن.

مثلاً یک GET ساده ممکنه بدون OPTIONS قبلی ارسال بشه.

اما اگر شرایط درخواست از قوانین درخواست ساده خارج بشه، مرورگر ممکنه قبلش Preflight انجام بده.

برای همین بعضی وقت‌ها توی Network می‌بینی:

OPTIONS POST

و میگی:

«این OPTIONS دیگه از کجا پیداش شد؟!» 😂

اون همون Preflight مربوط به CORSه.

پس CORS برای اذیت کردن ما ساخته نشده!

واقعیت اینه که CORS یکی از چیزهاییه که اول کار خیلی رو مخ به نظر میاد.

ولی اگر CORS وجود نداشت، یه سایت مخرب می‌تونست خیلی راحت از داخل مرورگر کاربر به سرویس‌های دیگه درخواست بفرسته و Response اون‌ها رو بخونه.

پس داستان اصلی اینه:

Browser ↓ "آیا این سایت اجازه داره؟" ↓ Server ↓ "آره، اجازه داره." ↓ Browser ↓ "باشه، Response رو در اختیار JavaScript می‌ذارم."

در واقع CORS یه مکانیزم اجازه دادن کنترل‌شده روی محدودیت Same-Origin Policy محسوب می‌شه.

CORS با Authentication چه فرقی داره؟

این یکی هم خیلی مهمه.

CORS نمیگه:

«آیا این کاربر اجازه دسترسی به اطلاعات رو داره؟»

این کار وظیفه Authentication و Authorization هست.

CORS بیشتر میگه:

«آیا JavaScriptای که از این Origin اجرا شده، اجازه داره Response این سرور رو بخونه؟»

پس اینا دو موضوع جدا هستن.

ممکنه CORS کاملاً درست تنظیم شده باشه، ولی کاربر Authentication نداشته باشه و API جواب 401 Unauthorized بده.

برعکسش هم ممکنه Authentication درست باشه، ولی مرورگر به خاطر CORS اجازه دسترسی به Response رو نده.

چرا Postman خطای CORS نمی‌ده؟

اینجا یکی از سؤال‌های معروفه:

«API توی Postman کار می‌کنه، ولی توی مرورگر CORS Error می‌گیرم. چرا؟»

چون CORS یک مکانیزم مربوط به مرورگره.

Postman مثل مرورگر رفتار نمی‌کنه و محدودیت‌های امنیتی مرورگر رو به همون شکل اعمال نمی‌کنه.

پس ممکنه:

Postman → API → OK ✅ Browser → API → CORS Error ❌

این لزوماً به معنی خراب بودن API نیست.

در نهایت CORS چرا داریم؟

اگر بخوایم همه داستان رو در یک جمله خلاصه کنیم:

CORS داریم تا مرورگر بتونه بین درخواست‌های Cross-Origin، دسترسی JavaScript به Responseها رو به شکل کنترل‌شده مدیریت کنه.

بدون این محدودیت‌ها، یک سایت مخرب می‌تونست از داخل مرورگر کاربر به سرویس‌های مختلف درخواست بفرسته و اطلاعات Response رو بخونه.

پس دفعه بعد که با این خطا مواجه شدی:

Blocked by CORS policy

به جای اینکه بگی:

«باز این CORS لعنتی!» 😂

یادت باشه مرورگر داره میگه:

«من فقط دارم مطمئن می‌شم این سایت واقعاً اجازه داره جواب این سرور رو بخونه.»

و دقیقاً به همین دلیل، CORS وجود داره.

corsامنیت وبفرانت اندبرنامه نویسیapi
۱
۰
Davood Habbabi
Davood Habbabi
اگر وقت کنم چیزایی که یاد گرفتم رو می نویسم
شاید از این پست‌ها خوشتان بیاید