CORS مخفف Cross-Origin Resource Sharing است و مکانیزمی مبتنی بر هدرهای HTTP برای کنترل دسترسی اسکریپت‌های یک مبدا (Origin) به منابع مبدا دیگر است.

مرورگر به‌صورت پیش‌فرض اجازه نمی‌دهد اسکریپت یک مبدا، پاسخ HTTP مبدا دیگری را بخواند. اگر سرور مقصد دسترسی را با هدرهای CORS مجاز کند، مرورگر پاسخ را در اختیار اسکریپت قرار می‌دهد. برای بعضی درخواست‌ها نیز مرورگر قبل از ارسال درخواست اصلی، یک درخواست Preflight از نوع OPTIONS ارسال می‌کند تا مشخص شود متد و هدرهای موردنظر مجاز هستند. (MDN)

در CDN ستون، می‌توانیم سیاست CORS را روی قانون مسیر تنظیم کنیم تا Edge برای درخواست‌های منطبق، هدرهای موردنیاز CORS را به پاسخ اضافه کند و در صورت نیاز به Preflight، درخواست OPTIONS را در Edge پاسخ دهد.


قانون یکسانی مبدا

مرورگر برای تشخیص اینکه دو URL از یک مبدا هستند، سه مولفه را بررسی می‌کند:

  • پروتکل
  • Hostname
  • پورت

اگر یکی از این مولفه‌ها متفاوت باشد، دو URL از مبداهای متفاوت هستند.

برای مثال، این URLها مبداهای متفاوتی دارند:

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

برای مثال، اگر صفحه‌ای از https://app.example.com بارگذاری شده باشد و JavaScript آن بخواهد با fetch به https://api.example.com درخواست بفرستد، این درخواست بین‌مبدا (Cross-Origin) است. مرورگر برای در اختیار گذاشتن پاسخ به اسکریپت، قواعد CORS را بررسی می‌کند. (MDN)

CORS به معنی اجازه دادن به درخواست از هر مبدا نیست. سرور یا Edge می‌تواند مشخص کند کدام Originها، متدها و هدرها مجاز هستند.


CORS چگونه کار می‌کند؟

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

برای مثال:

Origin: https://app.example.com

سرور یا Edge می‌تواند در پاسخ مشخص کند این Origin مجاز است:

Access-Control-Allow-Origin: https://app.example.com

درخواست فقط زمانی برای اسکریپت قابل خواندن است که پاسخ الزامات CORS موردنیاز مرورگر را داشته باشد. اگر CORS رد شود، درخواست HTTP لزوما در شبکه متوقف نمی‌شود؛ ممکن است سرور پاسخ را ارسال کرده باشد، اما مرورگر آن را در اختیار JavaScript قرار ندهد. (MDN)

بنابراین CORS را نباید با فایروال یا کنترل دسترسی در سمت سرور یکی بدانیم. CORS مکانیزمی است که مرورگر برای کنترل دسترسی اسکریپت به پاسخ اعمال می‌کند.


یک سناریوی واقعی

فرض کنیم یک SPA روی:

https://app.example.com

داریم و API آن روی:

https://api.example.com

قرار گرفته است. چون Hostname این دو URL متفاوت است، درخواست‌های SPA به API بین‌مبدا هستند.

فرض می‌کنیم API:

  • از متدهای GET، POST، PUT و DELETE استفاده می‌کند.
  • هدر Authorization را برای احراز هویت دریافت می‌کند.
  • برای احراز هویت از Cookie نیز استفاده می‌کند.

در این حالت، تنظیمات CORS باید Origin برنامه را مجاز کند و امکان استفاده از متدها، هدرها و Credentialهای موردنیاز را فراهم کند:

گزینهمقدار
Originهای مجازhttps://app.example.com
متدهای مجازGET، POST، PUT، DELETE
هدرهای مجازContent-Type، Authorization
Allow Credentialفعال
هدرهای Exposedدر صورت نیاز به خواندن هدرهای سفارشی پاسخ
Max age86400

برای مثال، اگر SPA یک درخواست POST همراه با هدر Authorization ارسال کند، مرورگر ممکن است ابتدا یک Preflight از نوع OPTIONS بفرستد. Edge با بررسی تنظیمات CORS مشخص می‌کند که Origin، متد و هدرهای موردنظر مجاز هستند و در صورت موفق بودن Preflight، مرورگر درخواست اصلی را ارسال می‌کند.

در این سناریو، OPTIONS را در متدهای مجاز قرار نمی‌دهیم؛ این متد برای انجام Preflight استفاده می‌شود و بخشی از متدهای API نیست.

اگر API از Cookie برای احراز هویت استفاده کند، Allow Credential را فعال می‌کنیم و به‌جای *، Origin دقیق برنامه را در فهرست Originهای مجاز قرار می‌دهیم.

برای آشنایی با نحوه پیکربندی این سناریو، فعال‌سازی CORS روی قانون مسیر را دنبال می‌کنیم.


درخواست ساده و Preflight

همه درخواست‌های بین‌مبدا Preflight ندارند.

درخواست ساده

برخی درخواست‌ها بدون ارسال Preflight مستقیما به مقصد ارسال می‌شوند. در اصطلاح رایج به این درخواست‌ها Simple Request گفته می‌شود.

برای مثال، یک GET معمولی می‌تواند بدون Preflight ارسال شود:

GET /users HTTP/1.1
Host: api.example.com
Origin: https://app.example.com

Edge یا آپ‌استریم پاسخ را برمی‌گرداند و پاسخ باید هدر CORS مناسب، مانند Access-Control-Allow-Origin، داشته باشد تا مرورگر آن را در اختیار اسکریپت قرار دهد. (MDN)

Preflight

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

برای مثال، درخواست PUT یا درخواستی که شامل هدر سفارشی مانند Authorization است، می‌تواند باعث Preflight شود.

مرورگر ابتدا یک درخواست OPTIONS ارسال می‌کند:

OPTIONS /users HTTP/1.1
Host: api.example.com
Origin: https://app.example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Authorization

سرور یا Edge باید مشخص کند که Origin، متد و هدرهای موردنظر مجاز هستند:

Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, PUT
Access-Control-Allow-Headers: Authorization

اگر پاسخ Preflight شرایط موردنیاز را داشته باشد، مرورگر درخواست اصلی را ارسال می‌کند. (MDN)

نگهداری نتیجه Preflight

هدر Access-Control-Max-Age به مرورگر اعلام می‌کند نتیجه Preflight را برای چه مدتی می‌تواند نگه دارد و در این مدت برای درخواست‌های مشابه دوباره Preflight ارسال نکند. مقدار نهایی ممکن است تحت محدودیت داخلی مرورگر نیز قرار بگیرد. (MDN)


چرا CORS روی قانون مسیر تنظیم می‌شود؟

CORS معمولا فقط برای بخشی از یک دامنه لازم است. برای مثال، ممکن است:

https://app.example.com

صفحه اصلی برنامه باشد و API در:

https://api.example.com

قرار داشته باشد.

اگر API پشت CDN باشد، می‌توانیم سیاست CORS را روی قانون مسیری که درخواست‌های API را دریافت می‌کند اعمال کنیم؛ برای مثال:

/api/*

در این حالت، سیاست CORS فقط روی درخواست‌های منطبق با این قانون اعمال می‌شود و لازم نیست آن را روی همه مسیرهای سایت فعال کنیم.

قانون مسیر مشخص می‌کند کدام درخواست‌ها با سیاست CORS مطابقت داشته باشند و تنظیمات CORS مشخص می‌کند چه Originها، متدها و هدرهایی مجاز هستند.


Origin مجاز

می‌توانیم یک یا چند Origin را به‌عنوان Origin مجاز تعیین کنیم.

برای مثال:

https://app.example.com
https://admin.example.com

وقتی Origin درخواست با یکی از مقادیر مجاز مطابقت داشته باشد، Edge می‌تواند همان Origin را در Access-Control-Allow-Origin پاسخ قرار دهد:

Access-Control-Allow-Origin: https://app.example.com

اگر Origin درخواست مجاز نباشد، Edge هدرهای لازم برای تایید CORS را در پاسخ قرار نمی‌دهد و مرورگر پاسخ را در اختیار اسکریپت قرار نمی‌دهد.

این رفتار با مسدود کردن درخواست در فایروال متفاوت است؛ CORS یک کنترل مرورگر برای دسترسی اسکریپت به پاسخ است.


CORS و اعتبارنامه

درخواست‌های بین‌مبدا ممکن است با اعتبارنامه (Credentials) مانند Cookie یا اطلاعات احراز هویت ارسال شوند. در این حالت، تنظیمات CORS باید اجازه استفاده از اعتبارنامه را نیز مشخص کند.

برای درخواست‌های Credentialed، استفاده از * به‌عنوان مقدار Access-Control-Allow-Origin مجاز نیست. باید Origin مشخصی در پاسخ قرار گیرد. (MDN)

برای مثال:

Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true

در نتیجه، اگر API از Cookie برای احراز هویت استفاده می‌کند، Origin دقیق برنامه را در فهرست Originهای مجاز قرار می‌دهیم و به wildcard * اکتفا نمی‌کنیم.

همچنین سیاست‌های Cookie مرورگر، از جمله محدودیت‌های مربوط به Third-Party Cookie، مستقل از CORS هستند و همچنان اعمال می‌شوند. (MDN)


CORS و کش

اگر پاسخ CORS بر اساس Origin تغییر کند، کش باید این تفاوت را نیز در نظر بگیرد. برای مثال، اگر یک پاسخ برای:

https://app.example.com

با پاسخ همان URL برای:

https://admin.example.com

تفاوت داشته باشد، نباید پاسخ اول بدون درنظرگرفتن Origin به درخواست دوم ارایه شود.

در چنین سناریوهایی، پاسخ‌های CORS معمولا به Vary: Origin نیاز دارند تا کش‌ها بدانند پاسخ به Origin درخواست وابسته است. MDN نیز در نمونه‌های CORS پویا از Vary: Origin استفاده می‌کند. (MDN)

نحوه ترکیب تنظیمات CORS با کش در CDN ستون به تنظیمات کش و نحوه مدیریت هدرهای پاسخ بستگی دارد. برای جزییات، کش در CDN ستون چگونه کار می‌کند؟ و مرجع تنظیمات CORS را مطالعه می‌کنیم.


CORS چه چیزی را کنترل نمی‌کند؟

CORS یک مکانیزم امنیتی برای مرورگر است و نباید آن را جایگزین کنترل دسترسی سمت سرور در نظر بگیریم.

برای مثال، اگر یک API فقط باید در اختیار کاربران احراز هویت‌شده باشد، صرفا محدود کردن Originهای مجاز کافی نیست. کنترل دسترسی و احراز هویت باید در خود API نیز انجام شود.

همچنین CORS مانع ارسال درخواست HTTP توسط ابزارهایی مانند curl یا سرویس‌های سمت سرور نمی‌شود. محدودیت اصلی CORS مربوط به نحوه رفتار مرورگر با درخواست‌های بین‌مبدا و دسترسی JavaScript به پاسخ است. (MDN)


ادامه مسیر

منابع