پرش به محتوای اصلی
آسیب‌پذیری‌های سایت

CORS چیست؟ بررسی تنظیمات اشتباه CORS و خطرات امنیتی آن

CORS یا Cross-Origin Resource Sharing مکانیزمی مبتنی بر HTTP Header است که تعیین می‌کند JavaScript کدام Originها اجازه دارد Response یک سایت یا API را به‌صورت Cross-Origin بخواند. تنظیمات اشتباه CORS مانند Reflect کردن Origin بدون Validation، اعتماد به Originهای غیرمجاز، Credentialed CORS بیش از حد باز یا Allowlist ناامن می‌تواند باعث افشای اطلاعات حساس شود.

تیم امنیت رخنه‌کاو ۴۳ دقیقه مطالعه انتشار:

CORS یا Cross-Origin Resource Sharing مکانیزمی مبتنی بر HTTP Header است که مشخص می‌کند JavaScript اجراشده در یک Origin تحت چه شرایطی اجازه دارد Response یک Origin دیگر را بخواند. CORS برای شکستن کنترل‌شده محدودیت Same-Origin Policy طراحی شده است؛ اما اگر تنظیمات آن بیش از حد باز، اشتباه یا مبتنی بر اعتماد نادرست به Origin باشد، ممکن است سایت به Domainهای غیرمجاز اجازه دهد اطلاعات API یا داده‌های متعلق به کاربران را از داخل مرورگر بخوانند.

به زبان ساده، CORS به Server می‌گوید:

«کدام وب‌سایت‌ها اجازه دارند از داخل Browser به این Resource دسترسی Cross-Origin داشته باشند؟»

اگر پاسخ به این سؤال اشتباه طراحی شود، Origin مهاجم نیز ممکن است در فهرست Originهای مورداعتماد قرار بگیرد.

خطر زمانی جدی‌تر می‌شود که API اطلاعات حساس برگرداند و Cross-Origin Request بتواند همراه Credentialهایی مانند Cookie یا Authentication Context ارسال شود. در چنین شرایطی یک CORS Misconfiguration می‌تواند باعث شود JavaScript سایتی غیرمجاز Responseهایی را بخواند که در حالت عادی Same-Origin Policy باید از دسترس آن خارج نگه دارد.

با این حال باید یک سوءبرداشت مهم را از همان ابتدا اصلاح کنیم:

CORS یک سیستم Authentication یا Authorization نیست.

CORS همچنین یک Web Application Firewall نیست.

CORS عمدتاً یک سیاست Browser-enforced برای کنترل Sharing Response میان Originها است. Clientهایی مانند Server-side Script، curl یا بسیاری از ابزارهای غیرمرورگری الزاماً محدودیت CORS مرورگر را ندارند. بنابراین API باید حتی در صورت داشتن CORS بسیار سخت‌گیرانه، همچنان Authentication، Authorization و سایر کنترل‌های امنیتی واقعی را در سمت Server اجرا کند.

در این مقاله رخنه‌کاو بررسی می‌کنیم CORS چیست، Same-Origin Policy چگونه کار می‌کند، Preflight چیست، Origin چگونه تعیین می‌شود، Headerهای اصلی CORS چه کاربردی دارند و مهم‌تر از همه، تنظیمات اشتباه CORS چگونه می‌توانند امنیت سایت، API و حساب کاربران را به خطر بیندازند.

CORS چیست؟

CORS مخفف Cross-Origin Resource Sharing است.

هدف CORS این است که یک Server بتواند به Browser اعلام کند کدام Originهای دیگر اجازه دارند Responseهای آن را از طریق APIهایی مانند fetch() و XMLHttpRequest بخوانند.

فرض کنید Frontend سایت روی این Origin قرار دارد:

https://app.example.com

و API روی Origin دیگری قرار گرفته است:

https://api.example.com

از دید Same-Origin Policy این دو Origin یکسان نیستند، زیرا Hostname متفاوتی دارند.

اگر JavaScript برنامه تلاش کند از app.example.com به api.example.com Request ارسال کند، Browser برای اینکه بداند آیا Response باید در اختیار JavaScript قرار بگیرد، CORS Policy Server مقصد را بررسی می‌کند.

Server ممکن است بگوید:

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

در این حالت Browser می‌تواند Response را در اختیار Script مربوط به Origin مجاز قرار دهد.

اما اگر درخواست از:

https://attacker.example

ارسال شده باشد و این Origin در Policy مجاز نباشد، Browser Response را در اختیار JavaScript آن Origin قرار نمی‌دهد.

MDN، CORS را مکانیزمی مبتنی بر HTTP Header تعریف می‌کند که به Server امکان می‌دهد Originهایی غیر از Origin خودش را برای دسترسی Cross-Origin مجاز کند. مرورگرها برای Requestهای Script-based مانند fetch() و XMLHttpRequest از این مکانیزم در کنار Same-Origin Policy استفاده می‌کنند.

Origin چیست؟

برای فهم CORS باید ابتدا Origin را دقیق بشناسیم.

Origin از سه مؤلفه تشکیل می‌شود:

Scheme یا Protocol

Hostname

Port

برای مثال:

https://example.com:443

با:

http://example.com:80

یک Origin نیست.

همچنین:

https://app.example.com

با:

https://api.example.com

Origin متفاوتی دارد.

حتی اگر هر دو متعلق به یک شرکت باشند.

MDN تعریف می‌کند دو Resource زمانی Same-Origin هستند که Scheme، Hostname و Port آن‌ها یکسان باشد. تفاوت Path به‌تنهایی Origin را تغییر نمی‌دهد.

چند مثال

آدرس اولآدرس دوموضعیت
https://example.com/page1https://example.com/page2Same-Origin
https://example.comhttp://example.comCross-Origin
https://example.comhttps://api.example.comCross-Origin
https://example.com:443https://example.com:8443Cross-Origin
https://example.com/ahttps://example.com/bSame-Origin

این تعریف دقیق اهمیت زیادی دارد، زیرا Allowlistهای CORS باید براساس Origin واقعی طراحی شوند، نه صرفاً شباهت اسمی Domainها.

Same-Origin Policy چیست؟

Same-Origin Policy یا SOP یکی از کنترل‌های بنیادی امنیت مرورگرها است.

هدف آن جلوگیری از این است که JavaScript یک سایت بتواند آزادانه اطلاعات سایت دیگری را بخواند.

فرض کنید شما همزمان وارد حساب ایمیل خود شده‌اید و سپس یک سایت ناشناس را باز می‌کنید.

اگر Same-Origin Policy وجود نداشت، JavaScript سایت ناشناس می‌توانست تلاش کند مستقیماً به صفحات حساب ایمیل شما Request ارسال کند و Response را بخواند.

این موضوع Privacy و Security وب را به‌شدت تضعیف می‌کرد.

Same-Origin Policy به‌طور پیش‌فرض دسترسی Script به Resourceهای Origin دیگر را محدود می‌کند.

CORS راهی کنترل‌شده برای Relax کردن همین محدودیت است.

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

Same-Origin Policy = حالت پیش‌فرض محدودکننده

CORS = مجوز کنترل‌شده برای اشتراک Resource میان Originهای مشخص

MDN نیز تأکید می‌کند CORS راهی است که Server از طریق آن به مرورگر اعلام می‌کند می‌خواهد بخشی از محدودیت‌های SOP را برای Originهای موردنظر خود بردارد.

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

این نکته بسیار مهم است:

CORS عمدتاً کنترل می‌کند آیا JavaScript یک Origin اجازه دارد Response یک Cross-Origin Request را بخواند یا خیر.

این جمله با:

«آیا Request می‌تواند به Server برسد؟»

یکسان نیست.

در بعضی شرایط Browser می‌تواند Cross-Origin Request را ارسال کند اما اجازه ندهد JavaScript پاسخ را بخواند.

به همین دلیل نباید CORS را Authorization Server تصور کرد.

برای مثال یک Server نباید بگوید:

«چون CORS فقط example.com را Allow کرده‌ام، پس دیگر لازم نیست Authorization روی Endpoint انجام دهم.»

این یک طراحی اشتباه جدی است.

یک Client غیرمرورگری می‌تواند مستقیماً با Endpoint تعامل داشته باشد و CORS Policy مرورگر برای آن معنایی ندارد.

OWASP نیز هشدار می‌دهد نباید فقط به Origin Header برای Access Control تکیه کرد و Application باید کنترل‌های امنیتی واقعی خود را مستقل از CORS اجرا کند. CORS چگونه کار می‌کند؟

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

مدل ساده CORS را می‌توان در چهار مرحله خلاصه کرد.

مرحله اول: JavaScript یک Request Cross-Origin ایجاد می‌کند

مثلاً Frontend روی:

https://shop.example.com

می‌خواهد از:

https://api.example.net

اطلاعات دریافت کند.

مرحله دوم: Browser Origin درخواست‌کننده را مشخص می‌کند

در Request ممکن است Header زیر دیده شود:

Origin: https://shop.example.com

مرحله سوم: Server درباره Origin تصمیم می‌گیرد

Server Origin را با Policy خودش مقایسه می‌کند.

اگر مجاز باشد، Response می‌تواند شامل Header زیر باشد:

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

مرحله چهارم: Browser Policy را اجرا می‌کند

اگر Headerها با CORS Protocol سازگار باشند، JavaScript می‌تواند Response را بخواند.

در غیر این صورت Browser دسترسی Script به Response را Block می‌کند.

بنابراین تصمیم نهایی درباره Exposure Response در Browser اجرا می‌شود، اما Policy توسط Server مشخص می‌شود.

Header Origin چیست؟

Origin یک HTTP Request Header است که Origin آغازکننده Request را مشخص می‌کند.

برخلاف Referer، Origin معمولاً Path کامل Page را ارسال نمی‌کند و فقط Security Context مرتبط با Origin را منتقل می‌کند.

مثلاً:

Origin: https://dashboard.example.com

Server می‌تواند این مقدار را با Allowlist خودش مقایسه کند.

اما یک نکته امنیتی مهم وجود دارد:

Origin Header نباید جای Authentication و Authorization را بگیرد.

OWASP یادآوری می‌کند Requestهای خارج از Browser می‌توانند شرایط متفاوتی داشته باشند و Application نباید فقط Origin را به‌عنوان اثبات هویت Client در نظر بگیرد.

Access-Control-Allow-Origin چیست؟

مهم‌ترین CORS Response Header معمولاً:

Access-Control-Allow-Origin

است.

این Header به Browser اعلام می‌کند Response برای کدام Origin قابل Share شدن است.

مثلاً:

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

یعنی JavaScript اجراشده از https://app.example.com می‌تواند در صورت رعایت سایر شرایط CORS به Response دسترسی داشته باشد.

برای Resource کاملاً عمومی ممکن است از:

Access-Control-Allow-Origin: *

استفاده شود.

اما Wildcard باید آگاهانه استفاده شود.

OWASP توصیه می‌کند Header تنها روی Resourceهایی فعال شود که واقعاً نیاز به Cross-Origin Access دارند و در حالت عادی Originهای مشخص و مورداعتماد به Allowlist اضافه شوند.

آیا Access-Control-Allow-Origin می‌تواند چند Domain داشته باشد؟

Access-Control-Allow-Origin به شکل رایج یک Origin مشخص یا * را در Response ارائه می‌کند.

اگر Server چند Origin مجاز داشته باشد، معمولاً Request Origin را دریافت می‌کند، آن را با Allowlist مقایسه می‌کند و در صورت Match شدن همان Origin تأییدشده را در Response برمی‌گرداند.

برای مثال Allowlist Server:

https://app.example.com

https://admin.example.com

https://partner.example.net

اگر Request از https://admin.example.com آمده باشد و Match معتبر باشد:

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

برگردانده می‌شود.

نکته حیاتی این است که Origin فقط بعد از Validation دقیق Reflect شود.

خطر Reflect کردن Origin بدون Validation

یکی از رایج‌ترین CORS Misconfigurationها چنین منطقی است:

Request دارای:

Origin: <whatever-origin>

باشد و Server بدون بررسی همان مقدار را در:

Access-Control-Allow-Origin

برگرداند.

در عمل Server می‌گوید:

«هر Originی که بگوید من هستم، مورداعتماد است.»

این رفتار تقریباً Allowlist را بی‌معنی می‌کند.

MDN برای Credentialed CORS صراحتاً توصیه می‌کند Originهای مشخص و مورداعتماد Allow شوند و Origin Header بدون Validation Reflect نشود.

مدل صحیح

  1. Origin Request دریافت شود.
  2. با لیست دقیق Originهای مورداعتماد مقایسه شود.
  3. تنها در صورت Match کامل در Response Reflect شود.
  4. در غیر این صورت CORS Header مناسب صادر نشود.

Wildcard یا * چه معنایی دارد؟

این Header:

Access-Control-Allow-Origin: *

به Browser می‌گوید Resource برای دسترسی Cross-Origin عمومی است.

برای Resource واقعاً Public مانند برخی APIهای عمومی، Assetها یا Dataهای فاقد اطلاعات حساس، این تنظیم ممکن است کاملاً منطقی باشد.

اما استفاده از Wildcard روی API خصوصی یا Resource حساس می‌تواند معماری امنیتی ضعیفی ایجاد کند.

MDN توصیه می‌کند * برای Public APIهای بدون Credential استفاده شود و Private APIها Originهای مشخص داشته باشند.

در Credentialed Request، Server نمی‌تواند از Wildcard در Access-Control-Allow-Origin استفاده کند.

برای مثال این ترکیب معتبر برای Exposure Credentialed Response نیست:

Access-Control-Allow-Origin: *

همراه:

Access-Control-Allow-Credentials: true

Browser چنین Responseای را برای Credentialed CORS به JavaScript ارائه نمی‌دهد.

در Requestهای Credentialed، Origin باید صریح باشد.

MDN و Fetch Standard هر دو این محدودیت را مشخص کرده‌اند.

این رفتار Browser یک Safety Control مهم است، اما نباید باعث شود Developer فکر کند هر CORS Configuration دیگری خودکار امن است.

Access-Control-Allow-Credentials چیست؟

این Header:

Access-Control-Allow-Credentials: true

اعلام می‌کند Server اجازه می‌دهد Response یک Cross-Origin Request که همراه Credential است برای Script مجاز قابل استفاده باشد.

Credential می‌تواند شامل مواردی مانند:

Cookie

HTTP Authentication

و در برخی Contextها Credentialهای تعریف‌شده توسط Protocol

باشد.

برای fetch()، Frontend می‌تواند در صورت نیاز از:

credentials: "include"

استفاده کند.

اما وجود Credentialed CORS سطح حساسیت Policy را افزایش می‌دهد.

اگر Origin مهاجم به اشتباه Allow شود و Credential نیز در Cross-site Context ارسال شود، Response حساس ممکن است در اختیار Script مهاجم قرار گیرد.

MDN تأکید می‌کند Credentialed Cross-Origin Request باید صریحاً توسط Server مجاز شود.

نکته مهم درباره Third-Party Cookies

CORS نمی‌تواند Policyهای Privacy Browser را Override کند.

حتی اگر Server:

Access-Control-Allow-Credentials: true

ارسال کند، Browser ممکن است براساس Policy مربوط به Third-party Cookie یا سایر Privacy Controlها Cookie را در Context موردنظر ارسال نکند.

MDN تصریح می‌کند Third-party Cookie Policies مستقل از CORS هستند و همچنان اجرا می‌شوند.

پس هنگام Debug کردن CORS نباید هر مشکل Cookie را مستقیماً به CORS نسبت داد.

Simple Request چیست؟

همه Cross-Origin Requestها Preflight ندارند.

برخی Requestهای ساده تحت شرایط مشخص می‌توانند مستقیماً ارسال شوند.

این نکته امنیتی بسیار مهم است زیرا بعضی Developers تصور می‌کنند:

«هر Cross-Origin Request ابتدا OPTIONS می‌زند؛ پس اگر OPTIONS را امن کنم کافی است.»

این تصور غلط است.

OWASP نیز هشدار می‌دهد Access Control واقعی باید روی Request اصلی انجام شود، زیرا نباید Preflight را جایگزین Authentication یا Authorization دانست. Preflight Request چیست؟

Preflight Request چیست؟

برای Requestهایی که از محدوده ساده CORS فراتر می‌روند، Browser ابتدا Requestی با Method:

OPTIONS

ارسال می‌کند.

این Request به Server می‌گوید Client قصد دارد چه Method و Headerهایی در Request واقعی استفاده کند.

مثلاً Browser ممکن است بپرسد:

آیا PUT مجاز است؟

آیا Header سفارشی خاص مجاز است؟

Server با CORS Response Headerها پاسخ می‌دهد.

در صورت مجاز بودن، Browser Request اصلی را ارسال می‌کند.

MDN Preflight را Request اولیه‌ای معرفی می‌کند که Browser برای بررسی مجاز بودن Method و Headerهای Request واقعی ارسال می‌کند.

آیا Preflight یک Security Authentication Step است؟

خیر.

Preflight جایگزین Authentication نیست.

جایگزین Authorization هم نیست.

Server باید Request واقعی را مستقل از نتیجه Preflight Authenticate و Authorize کند.

تصور کنید Developer روی OPTIONS Policy سخت‌گیرانه گذاشته اما Endpoint اصلی POST هیچ Authorization ندارد.

این API همچنان از نظر Access Control ناامن است.

Preflight بخشی از CORS Protocol است، نه سیستم Permission کسب‌وکار.

Access-Control-Allow-Methods چیست؟

این Header مشخص می‌کند در Cross-Origin Context چه HTTP Methodهایی مجاز هستند.

مثلاً:

Access-Control-Allow-Methods: GET, POST

اگر Application فقط GET و POST نیاز دارد، دلیلی ندارد Methodهای دیگر بدون نیاز مجاز شوند.

اصل Least Privilege در CORS هم کاربرد دارد.

Allow کردن:

GET

POST

PUT

PATCH

DELETE

بدون Requirement واقعی Attack Surface و پیچیدگی Policy را افزایش می‌دهد.

هرچند باید تأکید کرد این Header جای Authorization Method-specific در Backend را نمی‌گیرد.

Access-Control-Allow-Headers چیست؟

این Header در Response Preflight مشخص می‌کند Browser اجازه دارد در Request واقعی چه Headerهایی ارسال کند.

مثلاً Frontend ممکن است نیاز داشته باشد:

Content-Type

Authorization

یا یک Header اختصاصی ارسال کند.

Server باید فقط Headerهای لازم را Allow کند.

Allowlist بسیار گسترده بدون دلیل باعث می‌شود Trust Boundary Cross-Origin وسیع‌تر شود.

Access-Control-Expose-Headers چیست؟

Browser همه Response Headerها را به‌طور پیش‌فرض در اختیار JavaScript Cross-Origin قرار نمی‌دهد.

اگر Application نیاز دارد Header خاصی در JavaScript قابل خواندن باشد، Server می‌تواند از:

Access-Control-Expose-Headers

استفاده کند.

برای مثال شاید یک API نیاز داشته باشد Header Pagination یا Request ID را در اختیار Client قرار دهد.

در طراحی امنیتی باید بررسی شود Headerهای Exposed حاوی اطلاعات حساس نباشند.

Access-Control-Max-Age چیست؟

Preflight می‌تواند Performance Cost ایجاد کند.

Access-Control-Max-Age مشخص می‌کند نتیجه Preflight تا چه مدت Cache شود.

این قابلیت Performance را بهتر می‌کند، اما Policy Rotation را نیز باید در نظر گرفت.

اگر CORS Permission را تغییر دهید، Preflight Cache می‌تواند برای مدتی Policy قبلی را در Client حفظ کند.

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

مهم‌ترین خطر CORS Misconfiguration چیست؟

مهم‌ترین Risk زمانی ایجاد می‌شود که Server Origin غیرمجاز را به‌عنوان Origin مورداعتماد قبول کند و Resource حساس نیز Cross-Origin قابل خواندن باشد.

این وضعیت می‌تواند در سناریوهایی مانند موارد زیر رخ دهد:

Reflect کردن Origin بدون بررسی

Regex اشتباه

اعتماد به تمام Subdomainها

Allow کردن Origin با Scheme نامناسب

اعتماد به null

Credentialed CORS بیش از حد باز

Allowlist قدیمی و فراموش‌شده

Serverهای Development در Allowlist Production

CDN یا Proxy با Cache اشتباه CORS

Policy متفاوت بین Endpointها

این موارد را باید در Code Review و Security Test بررسی کرد.

Misconfiguration اول: Reflect کردن هر Origin

این یکی از بدترین الگوهاست.

منطق Application ممکن است چنین باشد:

«هر Origin که Request فرستاد، همان را در Access-Control-Allow-Origin برگردان.»

اگر Credentialed CORS نیز فعال باشد، Risk شدیدتر می‌شود.

Policy صحیح باید از Allowlist صریح استفاده کند.

OWASP توصیه می‌کند به‌جای Blind Reflection، فقط Originهای مشخص و Trusted مجاز شوند.

Misconfiguration دوم: اعتبارسنجی Origin با Contains

فرض کنید فقط:

https://example.com

مجاز است.

اما Developer برای Validation بررسی کند:

«آیا رشته Origin شامل example.com هست؟»

این روش به هیچ‌وجه Validation امن Origin نیست.

زیرا Domain دیگری می‌تواند رشته مشابهی در نام خود داشته باشد.

Validation باید Origin را Parse کند و Hostname، Scheme و Port مورد انتظار را دقیق بررسی کند.

Misconfiguration سوم: EndsWith اشتباه

بررسی Suffix نیز باید با دقت انجام شود.

برای مثال هدف این است که تمام Subdomainهای معتبر:

*.example.com

Allow شوند.

یک Check خام String می‌تواند Domainهایی با ساختار مشابه را اشتباه Accept کند.

راهکار بهتر استفاده از URL Parser معتبر و مقایسه Hostname براساس Boundary واقعی DNS است.

Misconfiguration چهارم: Regex بیش از حد باز

Regex یک ابزار قدرتمند است اما CORS Allowlist جای مناسبی برای Regex پیچیده و مبهم نیست.

برای مثال یک Pattern ضعیف ممکن است:

. را به‌درستی Escape نکند،

Scheme را بررسی نکند،

Port را نادیده بگیرد،

یا Domainهای ناخواسته را Match کند.

اگر تعداد Originهای مورداعتماد محدود است، Explicit Allowlist معمولاً قابل Auditتر و امن‌تر است.

Misconfiguration پنجم: اعتماد به تمام Subdomainها

گاهی سازمان CORS را برای:

*.example.com

باز می‌کند.

این تصمیم ممکن است منطقی به نظر برسد، اما یک سؤال مهم دارد:

آیا واقعاً تمام Subdomainها به یک اندازه Trusted هستند؟

ممکن است سازمان صدها Subdomain داشته باشد:

Old App

Marketing

User-generated Site

Deprecated Service

Cloud Host

Test Environment

اگر یکی از Subdomainهای مجاز Takeover شود یا امنیت ضعیف‌تری داشته باشد، CORS Trust آن می‌تواند به API حساس منتقل شود.

OWASP در CSRF guidance نیز هشدار می‌دهد Allow کردن تمام Subdomainها با Regex می‌تواند در صورت Subdomain Takeover خطرناک باشد.

Misconfiguration ششم: اعتماد به Origin null

گاهی Developer تصور می‌کند:

Origin: null

یعنی هیچ Originی وجود ندارد و بنابراین «امن» است.

اما این برداشت درست نیست.

Browser در برخی Contextهای Opaque Origin مانند بعضی file:، data: یا Sandboxed Documentها مقدار Origin را به null Serialize می‌کند.

MDN صراحتاً توصیه می‌کند:

Access-Control-Allow-Origin: null

استفاده نشود، زیرا Documentهای مخرب نیز ممکن است Origin null ایجاد کنند.

Misconfiguration هفتم: اعتماد به HTTP Origin برای HTTPS API

فرض کنید API حساس روی HTTPS است.

اگر CORS Allowlist نسخه HTTP سایت را نیز بدون نیاز واقعی Trust کند، Downgrade و Mixed Security Boundary ایجاد می‌شود.

OWASP توصیه می‌کند Requestهایی که از HTTP Origin به Resource حساس HTTPS می‌آیند با احتیاط بررسی شوند و Mixed Content/Security Issues در نظر گرفته شوند.

Scheme بخشی از Origin است و باید در Validation جدی گرفته شود.

Misconfiguration هشتم: فعال کردن CORS روی کل Domain

فرض کنید فقط /api/public/ نیاز به Cross-Origin Sharing دارد.

اما Administrator Header زیر را روی تمام Responseهای Site فعال می‌کند:

Access-Control-Allow-Origin: *

در نتیجه Pageها و Endpointهایی که اصلاً نیاز به Cross-Origin Access ندارند نیز وارد Policy می‌شوند.

OWASP توصیه می‌کند CORS فقط روی URLهایی فعال شود که واقعاً به آن نیاز دارند، نه لزوماً روی کل Domain.

Misconfiguration نهم: Access-Control-Allow-Credentials روی API عمومی

اگر API کاملاً Public است و هر Origin باید Data یکسانی دریافت کند، معمولاً نیازی به Credentialed CORS نیست.

اضافه کردن Credential بدون Requirement واقعی Trust Model را پیچیده‌تر می‌کند.

مدل ساده‌تر:

Public data

Access-Control-Allow-Origin: *

بدون Credential

اغلب امن‌تر و قابل فهم‌تر است.

Misconfiguration دهم: Vary: Origin فراموش می‌شود

اگر Server براساس Request Origin مقدار Access-Control-Allow-Origin را به‌صورت Dynamic تعیین کند، Response Cache باید بداند Response به Origin وابسته است.

MDN توصیه می‌کند در این حالت:

Vary: Origin

ارسال شود.

در غیر این صورت Cache یا CDN ممکن است Response Header تولیدشده برای یک Origin را به Client Origin دیگری تحویل دهد.

این موضوع مخصوصاً در Architectureهای دارای CDN اهمیت زیادی دارد.

CORS و Cache Poisoning یا Cache Confusion

CORS Policy Dynamic و Cache باید هماهنگ باشند.

فرض کنید Response برای Origin A چنین Headerی دارد:

Access-Control-Allow-Origin: https://a.example

اما Cache بدون توجه به Origin همان Response را Cache می‌کند.

Request Origin B ممکن است Response Header اشتباه دریافت کند.

نتیجه بسته به Configuration می‌تواند:

CORS Failure

Data Leakage

یا Behavior متناقض

باشد.

استفاده صحیح از Vary: Origin بخشی از طراحی CORS Dynamic است.

CORS و CDN

CDN می‌تواند CORS Header را:

Cache کند،

اضافه کند،

حذف کند،

Override کند.

به همین دلیل Security Review فقط Source Code Backend کافی نیست.

باید Response واقعی Production بررسی شود.

مسیر واقعی ممکن است:

Browser

→ CDN

→ WAF

→ Load Balancer

→ Reverse Proxy

→ Application

باشد.

Policy نهایی حاصل تعامل تمام این Layerها است.

آیا CORS از CSRF جلوگیری می‌کند؟

نه به‌طور کامل.

این یکی از مهم‌ترین سوءبرداشت‌هاست.

CORS و CSRF به یکدیگر مرتبط‌اند، اما هدفشان یکسان نیست.

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

CSRF درباره این است که Browser کاربر ناخواسته Request احراز هویت‌شده‌ای را به یک Site ارسال کند.

برخی Requestهای CSRF می‌توانند بدون نیاز به خواندن Response اثر خود را ایجاد کنند.

به همین دلیل OWASP تأکید می‌کند CORS جای CSRF Protection را نمی‌گیرد. تفاوت CORS و CSRF

تفاوت CORS و CSRF

موضوعCORSCSRF
هدف اصلیکنترل Cross-Origin Response Sharingجلوگیری از Request ناخواسته با Session کاربر
اجراکننده اصلیBrowser براساس Header ServerServer + Browser Controls
Controlهای رایجACAO، Credentials، PreflightCSRF Token، SameSite، Origin Check
آیا جای Authorization است؟خیرخیر
تمرکزRead/Interaction Cross-OriginForged State-changing Request

یک Application حساس ممکن است همزمان به CORS صحیح و CSRF Protection مناسب نیاز داشته باشد.

آیا Preflight جلوی CSRF را می‌گیرد؟

Preflight در بعضی Architectureها می‌تواند Attack Surface برخی Requestها را کاهش دهد، اما نباید تنها دفاع CSRF باشد.

Simple Requestها ممکن است Preflight نداشته باشند.

همچنین Authentication و Business Authorization باید همیشه روی Request واقعی اجرا شوند.

OWASP توصیه می‌کند Custom Header و Preflight می‌توانند بخشی از CSRF Defense برای API باشند، اما Originهای CORS باید به‌شدت محدود شوند.

تفاوت CORS و CSP

CORS و Content Security Policy یا CSP دو مکانیزم متفاوت هستند.

CORS می‌گوید:

«کدام Origin می‌تواند Response من را Cross-Origin بخواند؟»

CSP بیشتر به Browser می‌گوید:

«صفحه من اجازه دارد چه Resourceهایی را Load یا Execute کند؟»

برای مثال:

connect-src

در CSP می‌تواند مشخص کند JavaScript Page به چه Endpointهایی اتصال برقرار کند.

اما Server مقصد همچنان CORS Policy خودش را دارد.

در بعضی Architectureها استفاده همزمان از CSP و CORS Defense in Depth ایجاد می‌کند.

تفاوت CORS و CORP

Cross-Origin Resource Policy یا CORP مکانیزم دیگری است که به Resource اجازه می‌دهد نحوه Load شدن Cross-Origin را محدود کند.

نباید:

CORS

CORP

COEP

COOP

با یکدیگر اشتباه گرفته شوند.

همه آن‌ها به Cross-Origin Security مرتبط‌اند، اما اهداف متفاوتی دارند.

CORS مستقیماً روی Sharing Response Cross-Origin و Fetch-based Access تمرکز دارد.

CORS و Authentication

API باید جداگانه Authentication داشته باشد.

مثلاً:

Session Cookie

OAuth

Bearer Token

mTLS

یا Mechanism مناسب دیگر.

CORS فقط مشخص می‌کند Browser چه Responseای را در اختیار Script قرار دهد.

فرض کنید API چنین CORS Policy دارد:

فقط https://dashboard.example.com

مجاز است.

اما Endpoint هیچ Authentication ندارد.

هر Server-side Client می‌تواند مستقیماً Endpoint را Call کند.

بنابراین CORS نمی‌تواند API Authentication را تأمین کند.

CORS و Authorization

حتی User احراز هویت‌شده نیز نباید به همه Resourceها دسترسی داشته باشد.

API باید برای هر Object و Action Authorization را بررسی کند.

مثلاً:

User A نباید با دانستن ID یک Invoice متعلق به User B بتواند آن را بخواند.

CORS هیچ کمکی به این Object-level Authorization نمی‌کند.

اگر Authorization Fail باشد، API همچنان Broken Access Control دارد؛ حتی اگر CORS کامل باشد.

CORS و API Key

قرار دادن API Key محرمانه در Frontend و سپس محدود کردن CORS به Domain خودتان، API Key را Secret نمی‌کند.

هر چیزی که به Browser User ارسال شود باید بالقوه قابل مشاهده در نظر گرفته شود.

CORS برای پنهان کردن Secret در Client طراحی نشده است.

Secret Server-side باید در Server باقی بماند.

CORS و Bearer Token

اگر Frontend از Bearer Token استفاده می‌کند، Request با Header:

Authorization

اغلب Preflight نیاز خواهد داشت.

Server باید:

Origin را Validate کند،

Method را Validate کند،

Header مجاز را مشخص کند،

و در Request واقعی Token را Validate و Authorization را اجرا کند.

Preflight Success به معنی Valid بودن Token نیست.

CORS در Single Page Application

SPAها معمولاً Frontend و API جدا دارند.

مثلاً:

https://app.example.com

و:

https://api.example.com

در این Architecture CORS کاملاً طبیعی است.

روش امن‌تر:

Allowlist دقیق Frontend Production

عدم Allow کردن Development Origin در Production بدون نیاز

Credential Policy مشخص

Vary: Origin

HTTPS-only

Method Allowlist

Header Allowlist

Testing خودکار Configuration

است.

localhost در Production

یکی از اشتباهات رایج باقی گذاشتن Originهای Development در Production است:

http://localhost:3000

http://127.0.0.1:5173

این Originها ممکن است در Development لازم باشند اما باید بررسی شود آیا واقعاً در Production نیز نیاز هستند.

Trusting localhost Context می‌تواند پیامدهای امنیتی خاص خودش را داشته باشد، به‌خصوص اگر Application حساس Credentialed CORS داشته باشد.

Production Allowlist باید جداگانه مدیریت شود.

Environment Separation

Allowlist مناسب برای Development الزاماً مناسب Production نیست.

بهتر است CORS Config برای:

Development

Staging

Production

جدا باشد.

مثلاً:

Development → localhostهای مشخص

Staging → staging frontend

Production → فقط Originهای Production

کپی مستقیم Configuration میان Environmentها می‌تواند Originهای غیرضروری را Trust کند.

CORS در Multi-tenant SaaS

در SaaS ممکن است هر Tenant Domain اختصاصی داشته باشد.

مثلاً:

tenant1.customer.com

tenant2.customer.com

در این شرایط CORS Allowlist Dynamic پیچیده‌تر می‌شود.

Server نباید صرفاً هر Domainی را که Tenant وارد کرده Trust کند.

Domain Ownership Verification، Lifecycle و حذف Domain باید طراحی شود.

همچنین باید مشخص باشد:

آیا Tenant A می‌تواند API Tenant B را Cross-Origin بخواند؟

CORS باید همراه Tenant Authorization طراحی شود.

Wildcard Subdomain در SaaS

گاهی ساده‌ترین راه این به نظر می‌رسد:

تمام *.example.com را Trust کنیم.

اما اگر Platform اجازه User-created Subdomain می‌دهد، تمام کاربران می‌توانند Origin مورداعتماد CORS داشته باشند.

در چنین Architectureای Wildcard ممکن است کاملاً نامناسب باشد.

Trust Boundary CORS باید با Ownership Model Domain هماهنگ شود.

CORS در Microservice Architecture

CORS معمولاً باید در Edge Layer مدیریت شود، نه لزوماً بین تمام Microserviceهای داخلی.

Browser با Public Gateway تعامل دارد.

Service-to-service Communication داخل Backend اصولاً توسط Browser CORS کنترل نمی‌شود.

برای Internal Services باید از:

Service Authentication

Network Policy

mTLS در صورت نیاز

Authorization

استفاده شود.

استفاده از CORS برای «امن کردن Microserviceهای داخلی» مفهوم درستی نیست.

CORS و GraphQL

GraphQL Endpoint نیز همان اصول CORS را دارد.

اگر Frontend Origin متفاوت است باید CORS Policy تعریف شود.

اما GraphQL Security همچنان نیازمند:

Authentication

Authorization

Query Complexity Limit

Rate Limit

Input Validation

است.

CORS هیچ‌کدام از این کنترل‌ها را جایگزین نمی‌کند.

CORS و WebSocket

WebSocket CORS استاندارد fetch() را به همان شکل استفاده نمی‌کند.

WebSocket Security نیازمند بررسی Origin و Authentication خودش است.

بنابراین نباید تصور کرد CORS Middleware API تمام Cross-Origin Risks WebSocket را حل می‌کند.

OWASP نیز WebSocket Security را موضوعی جدا از CORS در راهنمای HTML5 Security معرفی می‌کند.

خطر Access-Control-Allow-Origin روی اطلاعات حساس

فرض کنید Endpoint:

/api/profile

اطلاعات حساس User Login شده را برگرداند.

اگر Origin غیرمجاز به اشتباه Allow شود و Credentials نیز در Context موردنظر ارسال شوند، Script Origin مهاجم می‌تواند Response را بخواند.

اما اگر Endpoint Public باشد و همان Data برای همه کاربران یکسان باشد، * الزاماً Vulnerability نیست.

بنابراین Severity به Data و Authentication Context وابسته است.

چگونه Risk واقعی CORS را ارزیابی کنیم؟

برای هر Endpoint پنج سؤال بپرسید:

  1. چه Originهایی Allow هستند؟
  2. Response چه Dataیی دارد؟
  3. آیا Credential همراه Request می‌تواند ارسال شود؟
  4. آیا User-specific Data برگردانده می‌شود؟
  5. اگر Origin مهاجم Response را بخواند Impact چیست؟

بدون پاسخ این سؤال‌ها صرف دیدن یک CORS Header برای تعیین Severity کافی نیست.

آیا Access-Control-Allow-Origin: * همیشه آسیب‌پذیری است؟

خیر.

اگر Endpoint عمداً Public است و Data حساس ندارد، Wildcard می‌تواند Configuration صحیح باشد.

مثلاً:

Public Documentation API

Public Asset

Open Data

مشکل زمانی است که * بدون درک Data Classification روی API خصوصی یا Endpointهای غیرضروری استفاده شود.

Security Finding باید Context را بررسی کند.

آیا نبود CORS Header یعنی سایت امن است؟

فقط از نظر Cross-Origin Response Sharing معمولاً Browser SOP محدودکننده باقی می‌ماند.

اما نبود CORS به این معنی نیست که Application از:

CSRF

XSS

Broken Access Control

SQL Injection

یا سایر Vulnerabilityها امن است.

CORS فقط یک بخش کوچک از Security Architecture است.

آیا CORS Error به معنی Server Error است؟

نه.

ممکن است Server Response کاملاً 200 باشد اما Browser به دلیل CORS آن را در اختیار JavaScript قرار ندهد.

Developer فقط خطای CORS در Console مشاهده می‌کند.

به همین دلیل Debug باید شامل:

Network Request

Response Header

Preflight

Origin

Credential Mode

باشد.

MDN نیز توضیح می‌دهد جزئیات CORS Failure معمولاً در Browser Console قابل مشاهده هستند.

چرا Postman کار می‌کند ولی Browser CORS Error می‌دهد؟

این یک سؤال بسیار رایج است.

Postman یا curl Browser نیستند و Same-Origin Policy مرورگر را به همان شکل اجرا نمی‌کنند.

بنابراین API ممکن است در Postman کاملاً پاسخ دهد اما JavaScript Browser نتواند Response را بخواند.

این موضوع به خودی خود نشان نمی‌دهد Server Down است.

نشان می‌دهد Cross-Origin Browser Policy باید بررسی شود.

آیا می‌توان mode: no-cors استفاده کرد تا مشکل حل شود؟

no-cors راهکار واقعی برای دسترسی JavaScript به API خصوصی Cross-Origin نیست.

در این Mode Response برای JavaScript به‌شکل محدود یا Opaque رفتار می‌کند و Application نمی‌تواند مانند CORS مجاز به Data Response دسترسی عادی داشته باشد.

استفاده از no-cors برای پنهان کردن Configuration Error معمولاً راه‌حل معماری نیست.

CORS باید سمت Server درست تنظیم شود.

اشتباه رایج: نصب Extension مرورگر برای Disable CORS

این کار ممکن است در Development موقت بعضی تست‌ها را ساده کند اما راهکار Production نیست.

کاربر واقعی Extension شما را ندارد.

همچنین Disable کردن Browser Security می‌تواند Developer را درباره Configuration واقعی گمراه کند.

بهتر است Backend Policy درست طراحی شود.

اشتباه رایج: Access-Control-Allow-Origin را از Frontend تنظیم کنیم

Access-Control-Allow-Origin Response Header است و باید توسط Server پاسخ‌دهنده یا Infrastructure آن تنظیم شود.

Frontend JavaScript نمی‌تواند از Origin خودش به Server دیگری دستور دهد:

«من را Allow کن.»

CORS Permission توسط Server مقصد اعلام می‌شود.

اشتباه رایج: Request Header با نام Access-Control-Allow-Origin

بعضی Developers تلاش می‌کنند:

Access-Control-Allow-Origin

را در Request بفرستند.

این Header متعلق به Response است.

Browser برای Request Headerهای CORS مکانیزم‌های خودش مانند:

Origin

Access-Control-Request-Method

Access-Control-Request-Headers

را مدیریت می‌کند.

اشتباه رایج: Allow-Origin: * برای رفع سریع Error

در Development ممکن است Developer برای حذف CORS Error مستقیماً * قرار دهد و همان Configuration وارد Production شود.

روش بهتر این است که Requirement واقعی را مشخص کنیم:

چه Originهایی؟

کدام Routeها؟

چه Methodهایی؟

Credential لازم است؟

سپس Policy حداقلی طراحی کنیم.

اشتباه رایج: Allow-Credentials همیشه true

اگر Endpoint به Credential Cross-Origin نیاز ندارد، Access-Control-Allow-Credentials را فعال نکنید.

هر Permission اضافی باید دلیل مشخص داشته باشد.

Least Privilege فقط مربوط به User Role نیست؛ Security Headerها هم باید حداقل دسترسی لازم را بدهند.

اشتباه رایج: Trust بر اساس Referer

CORS مبتنی بر Origin است.

Referer Semantics متفاوتی دارد و ممکن است Path داشته باشد یا براساس Privacy Policy محدود شود.

برای CORS Allowlist باید Mechanism استاندارد Origin به شکل صحیح Parse شود.

اشتباه رایج: مقایسه Raw String URL

URL Parsing از مواردی است که Edge Case زیاد دارد.

بهتر است Origin با URL Parser معتبر Parse شود و:

Scheme

Hostname

Port

جداگانه و Canonical بررسی شوند.

String Manipulation دستی احتمال Bug را افزایش می‌دهد.

CORS در WordPress

WordPress REST API دارای رفتار CORS داخلی است.

تابع رسمی:

rest_send_cors_headers()

برای Requestهای REST CORS Header ارسال می‌کند.

در نسخه فعلی Source این Function، Origin Request گرفته می‌شود و در صورت وجود، پس از پردازش مناسب در:

Access-Control-Allow-Origin

برگردانده می‌شود؛ همچنین Methodهای REST، Credential Support و Vary: Origin تنظیم می‌شوند.

بنابراین مدیران WordPress باید قبل از اضافه کردن Headerهای سفارشی بدانند Core چه رفتاری دارد.

WordPress REST API و Origin

مستندات رسمی WordPress توضیح می‌دهند REST API به‌صورت پیش‌فرض Incoming Origin را به‌عنوان مکانیزم CSRF Validation محدود نمی‌کند و برای Authentication مبتنی بر Cookie از Nonce استفاده می‌کند.

WordPress این رفتار را یک تصمیم طراحی معرفی می‌کند و اگر مدیر سایت بخواهد CORS Policy سخت‌گیرانه‌تری داشته باشد، امکان جایگزین کردن رفتار rest_send_cors_headers وجود دارد.

این نکته اهمیت تفاوت CORS و CSRF را دوباره نشان می‌دهد.

آیا باید CORS پیش‌فرض WordPress را تغییر دهیم؟

نه همیشه.

اگر WordPress REST API عمومی یا Integrationهای متعدد دارد، محدود کردن کورکورانه Originها می‌تواند Functionality را بشکند.

ابتدا مشخص کنید:

چه Endpointهایی Public هستند؟

کدام Endpoint Authentication دارند؟

چه Frontendهایی REST API را استفاده می‌کنند؟

آیا Headless Frontend وجود دارد؟

آیا Mobile App یا Integration خارجی دارید؟

بعد CORS Policy را براساس Architecture طراحی کنید.

CORS در Headless WordPress

Headless WordPress یکی از سناریوهای رایج CORS است.

مثلاً WordPress روی:

https://cms.example.com

و Frontend روی:

https://www.example.com

است.

چون Originها متفاوت‌اند، Browser ممکن است برای Fetch REST API به CORS نیاز داشته باشد.

Policy مناسب می‌تواند فقط Frontend Production را Allow کند.

اگر Preview Service یا Admin App دیگری وجود دارد، Origin آن نیز به‌صورت صریح اضافه شود.

CORS در WooCommerce

WooCommerce و Pluginهای آن ممکن است REST API یا Endpoint سفارشی داشته باشند.

موارد حساس شامل:

Customer Data

Order

Wallet

Subscription

Payment-related Metadata

هستند.

هر Endpoint باید مستقل بررسی شود.

فعال کردن CORS عمومی روی APIهای Customer-specific بدون بررسی Authentication و Data Exposure می‌تواند خطرناک باشد.

CORS در Nginx و Apache

CORS Header ممکن است در:

Application

Nginx

Apache

CDN

Load Balancer

Cloud Gateway

تنظیم شود.

مشکل زمانی رخ می‌دهد که چند Layer همزمان CORS را مدیریت کنند.

ممکن است دو Access-Control-Allow-Origin متفاوت ایجاد شود یا یک Layer Header دیگری را Override کند.

بهتر است Ownership CORS Policy واضح باشد:

چه Layerی Source of Truth است؟

Errorهای رایج Configuration در Reverse Proxy

نمونه مشکلات:

Backend Origin درست را Allow می‌کند اما Proxy آن را با * جایگزین می‌کند.

Proxy Header را فقط روی HTTP 200 می‌فرستد و Error Responseها CORS ندارند.

OPTIONS به Backend Route نمی‌رسد.

CDN OPTIONS را Cache نادرست می‌کند.

Vary: Origin حذف می‌شود.

Production Testing باید تمام Status Codeهای مهم را پوشش دهد.

CORS Error روی Responseهای 401 و 403

گاهی API Unauthorized Response را درست ایجاد می‌کند اما CORS Header روی 401/403 وجود ندارد.

Frontend در Browser به‌جای Error Business قابل فهم، فقط CORS Failure می‌بیند.

این موضوع بیشتر Reliability/Debugging است، اما می‌تواند Monitoring را سخت کند.

CORS Policy باید روی Error Responseهای موردنیاز نیز Consistent باشد.

CORS Error روی 500

Server نباید Internal Error Detail را فقط برای حل CORS به Client Cross-Origin نمایش دهد.

اگر 500 Response نیاز به CORS Header دارد، همچنان Error Message باید Safe باشد.

CORS و Secure Error Handling باید با هم طراحی شوند.

تست امنیت CORS چگونه انجام می‌شود؟

تست دفاعی CORS باید از Configuration Review شروع شود.

مرحله اول: Inventory

مشخص کنید کدام Endpointها CORS دارند.

مرحله دوم: Originهای مجاز

Allowlist را با Business Requirement مقایسه کنید.

مرحله سوم: Credentials

بررسی کنید Credentialed CORS واقعاً لازم است یا نه.

مرحله چهارم: Headerها

موارد زیر بررسی شوند:

Access-Control-Allow-Origin

Access-Control-Allow-Credentials

Access-Control-Allow-Methods

Access-Control-Allow-Headers

Access-Control-Expose-Headers

Vary: Origin

مرحله پنجم: Edge Caseها

بررسی کنید Policy چگونه با:

Subdomain

Port متفاوت

HTTP

null Origin

Origin ناشناخته

رفتار می‌کند.

مرحله ششم: Data Impact

مشخص کنید Response حاوی چه Dataیی است.

تست باید روی سیستم خودتان یا سامانه‌ای که برای آن مجوز دارید انجام شود.

Code Review برای CORS

در Code Review به دنبال موارد زیر باشید:

Blind Origin Reflection

Regex بیش از حد باز

Wildcard Credential Logic

Allowlist Hard-coded قدیمی

Development Origins

null Origin

Suffix Matching اشتباه

Substring Matching

CORS Middleware عمومی روی همه Routeها

Missing Vary Header

Duplicated CORS Logic

Code Review باید Configuration Infrastructure را نیز شامل شود، نه فقط Application Source. چک‌لیست امن CORS

چک‌لیست امن CORS

شناسایی نیاز

  • آیا واقعاً Cross-Origin Access لازم است؟
  • کدام Endpointها نیاز دارند؟
  • آیا تمام Site باید CORS داشته باشد؟
  • آیا Data Public است یا Private؟

Origin Allowlist

  • فقط Originهای مورداعتماد Allow شوند.
  • Scheme دقیق بررسی شود.
  • Hostname دقیق بررسی شود.
  • Port بررسی شود.
  • Raw Origin بدون Validation Reflect نشود.
  • Regex مبهم استفاده نشود.
  • null به‌عنوان Origin مورداعتماد استفاده نشود.
  • Subdomain Wildcard فقط با Threat Model استفاده شود.

Credentials

  • Access-Control-Allow-Credentials: true فقط در صورت نیاز فعال شود.
  • Credentialed CORS فقط برای Originهای دقیق Allow شود.
  • Wildcard با Credential استفاده نشود.
  • Cookie SameSite Policy بررسی شود.
  • Authorization Server-side همیشه اجرا شود.

Methods

  • فقط Methodهای لازم Allow شوند.
  • DELETE و PATCH بدون نیاز اضافه نشوند.
  • Request واقعی نیز Authorization داشته باشد.
  • OPTIONS به‌عنوان Authentication جایگزین دیده نشود.

Headers

  • فقط Headerهای لازم Allow شوند.
  • Authorization Header با Authentication واقعی Validate شود.
  • Response Headerهای حساس Expose نشوند.
  • Headerهای Application مستند باشند.

Cache

  • Dynamic Origin Response دارای Vary: Origin باشد.
  • CDN CORS Headerها را درست Cache کند.
  • OPTIONS Cache بررسی شود.
  • Configuration بعد از تغییر Cache Purge شود.

Environment

  • Production Allowlist مستقل باشد.
  • localhost در Production فقط با نیاز واقعی وجود داشته باشد.
  • Staging Originها وارد Production نشوند.
  • HTTP Originهای غیرضروری حذف شوند.

WordPress

  • رفتار REST API Core را قبل از Override بررسی کنید.
  • Pluginها CORS Header متناقض نسازند.
  • Headless Frontend Origin دقیق Allow شود.
  • WooCommerce APIهای حساس بررسی شوند.
  • CDN و Web Server Response نهایی تست شوند.

Monitoring

  • تغییر CORS Configuration Code-reviewed باشد.
  • Policy در CI/CD تست شود.
  • Originهای جدید مستند شوند.
  • Allowlist قدیمی دوره‌ای حذف شود.
  • Security Review پس از تغییر Domain یا CDN انجام شود.

CORS Policy نمونه برای API عمومی

اگر API عمداً Public است و هیچ Credential یا اطلاعات User-specific ندارد، Policy ساده می‌تواند مناسب باشد:

Access-Control-Allow-Origin: *

در این حالت بهتر است Credentialed CORS فعال نباشد.

MDN همین مدل را برای Resource عمومی پیشنهاد می‌کند.

CORS Policy نمونه برای Dashboard خصوصی

اگر API فقط باید از Dashboard مشخصی قابل استفاده باشد:

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

و اگر Credential Cross-Origin واقعاً نیاز است:

Access-Control-Allow-Credentials: true

همراه با:

Vary: Origin

و Authentication و Authorization واقعی در Backend.

این Policy نباید به شکل کورکورانه برای Domainهای دیگر Reflect شود.

چرا Vary: Origin مهم است؟

فرض کنید API براساس Origin Request Header متفاوت تولید می‌کند.

Response برای Site A:

Access-Control-Allow-Origin: https://a.example

و Response برای Site B:

Access-Control-Allow-Origin: https://b.example

Cache باید بفهمد این دو Response از نظر Origin یکسان نیستند.

Vary: Origin

همین نکته را به Cacheها اعلام می‌کند.

MDN استفاده از Vary را در CORS Dynamic Origin توصیه می‌کند.

امنیت CORS و Principle of Least Privilege

CORS نیز Permission System دارد.

سؤال این نیست:

«چه Originهایی شاید روزی لازم شوند؟»

سؤال باید این باشد:

«حداقل Originهایی که امروز برای Functionality ضروری هستند کدام‌اند؟»

همین منطق برای:

Methods

Headers

Credentials

Routes

نیز باید اعمال شود.

Policy کوچک‌تر:

قابل Auditتر،

قابل Testingتر،

و معمولاً امن‌تر

است.

CORS و Zero Trust

Zero Trust به این معنی نیست که CORS را غیرفعال کنیم.

بلکه به این معنی است که Origin Trusted بودن نباید باعث حذف Authentication و Authorization شود.

حتی Request از Frontend رسمی:

باید Authentication معتبر داشته باشد.

باید Permission بررسی شود.

باید Input Validation شود.

CORS فقط یکی از Signalها و Browser Policies است، نه هویت کامل Client.

آیا Origin Header قابل Spoof است؟

در Context عادی Browser CORS، Browser Origin Header را مدیریت می‌کند و JavaScript نمی‌تواند آزادانه مانند یک Header معمولی آن را تنظیم کند.

اما Server ممکن است Request از Clientهای غیرمرورگری نیز دریافت کند.

آن‌ها الزاماً تحت Browser Constraint نیستند.

بنابراین Origin نباید به‌تنهایی به‌عنوان Credential یا Authorization Evidence استفاده شود.

OWASP نیز همین مسئله را گوشزد می‌کند.

CORS و Mobile App

Native Mobile App معمولاً Same-Origin Policy Browser را ندارد.

اگر API تنها با CORS «محافظت» شده باشد، Mobile Client یا Script مستقیم همچنان می‌تواند Request ارسال کند.

بنابراین API Mobile باید Authentication و Authorization مستقل داشته باشد.

CORS برای Web Frontend است، نه جایگزین API Security.

CORS و Server-to-Server Request

Server-to-server Communication نیز عموماً تحت Browser CORS نیست.

اگر Backend شما به API دیگری Request می‌زند، CORS Header معمولاً نقش امنیتی مشابه Browser ندارد.

برای آن ارتباط باید از:

TLS

Authentication

Authorization

Network Controls

استفاده شود.

آیا CORS WAF است؟

خیر.

WAF Requestها را براساس Ruleهای امنیتی بررسی می‌کند.

CORS Browser Sharing Policy است.

WAF ممکن است CORS Headerها را مدیریت کند، اما مفاهیم یکسان نیستند.

حتی بهترین CORS Policy نمی‌تواند Injection یا Malware Request را Block کند.

آیا CORS باعث جلوگیری از Data Exfiltration می‌شود؟

CORS می‌تواند Browser JavaScript غیرمجاز را از خواندن Response Cross-Origin بازدارد.

اما اگر Data از مسیر دیگری در دسترس مهاجم باشد، CORS آن را حل نمی‌کند.

مثلاً:

Endpoint بدون Authentication

XSS در Origin مجاز

Compromised Trusted Origin

Server-side Request

CORS Protection کافی نخواهد بود.

به همین دلیل Trusted Originها نیز باید امن باشند.

XSS در Origin مجاز و CORS

فرض کنید CORS فقط:

https://app.example.com

را Trust می‌کند.

اگر همان Frontend XSS جدی داشته باشد، مهاجم می‌تواند Script را داخل Origin مورداعتماد اجرا کند.

از دید CORS Request کاملاً از Origin مجاز می‌آید.

این مثال نشان می‌دهد CORS باید همراه:

XSS Prevention

CSP

Secure Coding

Authorization

استفاده شود.

Subdomain Takeover و CORS

اگر CORS تمام Subdomainها را Allow کند و یکی از Subdomainهای قدیمی Takeover شود، مهاجم ممکن است Originی به دست آورد که Server آن را Trusted می‌داند.

به همین دلیل Asset Inventory و Domain Lifecycle بخشی از CORS Security است.

Subdomain حذف‌شده از Business باید از Allowlist نیز حذف شود.

CORS Policy باید چه زمانی بازبینی شود؟

زمان‌های مهم:

اضافه شدن Frontend جدید

Migration Domain

تغییر CDN

مهاجرت HTTP به HTTPS

اضافه شدن Mobile Web

Headless Migration

اضافه شدن Partner

حذف Integration

تغییر Authentication

Incident امنیتی

Subdomain Retirement

CORS Configuration یک تنظیم «یک بار برای همیشه» نیست.

اشتباهات رایج CORS

اعتماد به تمام Originها

بدون Requirement واقعی خطرناک است.

Reflect کردن Origin

بدون Allowlist امن نیست.

اعتماد به null

می‌تواند Opaque Originهای غیرمنتظره را Trust کند.

CORS به‌جای Authentication

مدل امنیتی اشتباه است.

CORS به‌جای CSRF Protection

کافی نیست.

Wildcard روی Private API

Exposure غیرضروری ایجاد می‌کند.

Credentials برای همه

Trust Boundary را بیش از نیاز باز می‌کند.

Allow کردن همه Subdomainها

در برابر Subdomain Compromise حساس است.

فراموش کردن Vary

Cache Behavior را خراب می‌کند.

استفاده از Regex پیچیده

Audit و Validation را دشوار می‌کند.

تنظیم چندگانه در Application و Proxy

Responseهای متناقض ایجاد می‌کند.

نگه داشتن localhost

Production Trust غیرضروری ایجاد می‌کند.

Fix کردن فقط Frontend

CORS Permission باید در Server مقصد مدیریت شود.

آیا CORS می‌تواند اطلاعات داخلی شبکه را در معرض خطر قرار دهد؟

Fetch Standard دلیل Opt-in بودن CORS را از جمله جلوگیری از Exposure داده‌های Cross-Origin و Resourceهای حساس پشت شبکه‌های داخلی می‌داند.

Browser Security در سال‌های اخیر برای Private Network Access نیز تکامل یافته است.

اما Organization نباید Security شبکه داخلی را فقط به SOP/CORS بسپارد.

Internal API نیز باید Authentication و Network Controls داشته باشد.

آیا CORS Misconfiguration همیشه Critical است؟

خیر.

Severity به Context بستگی دارد.

سه مثال:

حالت اول

Access-Control-Allow-Origin: *

روی فایل Public Logo.

Risk بسیار کم.

حالت دوم

Origin Reflection روی API Public بدون Credential و بدون Data حساس.

Configuration بد است اما Impact محدودتر.

حالت سوم

Origin Reflection + Credentialed Requests + User-specific Sensitive API.

Risk می‌تواند بسیار جدی باشد.

بنابراین Finding امنیتی باید Data Impact را مشخص کند.

چگونه CORS Finding را گزارش کنیم؟

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

Affected Endpoint

Observed Policy

Trusted Origin Logic

Credential Behavior

Data Sensitivity

Affected User Context

Security Impact

Recommended Allowlist

Infrastructure Layer

Retest Result

صرف نوشتن «CORS اشتباه است» برای تیم توسعه کافی نیست.

CORS و E-E-A-T سایت

CORS مستقیماً معیار رتبه‌بندی سئو نیست.

اما API Security بخشی از Reliability و Trust واقعی یک سرویس است.

وب‌سایتی که اطلاعات کاربران را به Originهای غیرمجاز ارائه دهد، از نظر اعتماد و مدیریت داده ضعف جدی دارد.

به‌خصوص برای:

فروشگاه

سایت مالی

پنل سازمانی

CRM

SaaS

داشبورد کاربران

امنیت API بخشی از کیفیت واقعی محصول است.

سوالات متداول درباره CORS

CORS چیست؟

CORS یا Cross-Origin Resource Sharing مکانیزمی مبتنی بر HTTP Header است که به Server اجازه می‌دهد مشخص کند JavaScript کدام Originهای دیگر می‌تواند Responseهای آن را Cross-Origin بخواند. Same-Origin Policy چیست؟

Same-Origin Policy چیست؟

SOP سیاست امنیتی Browser است که دسترسی Scriptها به Resourceهای Originهای دیگر را به‌طور پیش‌فرض محدود می‌کند. Origin براساس Scheme، Hostname و Port تعیین می‌شود.

آیا CORS یک سیستم امنیتی است؟

CORS بخشی از Browser Security است، اما Authentication یا Authorization نیست. Backend باید تمام کنترل‌های دسترسی واقعی را مستقل از CORS انجام دهد.

Access-Control-Allow-Origin چیست؟

Response Header اصلی CORS است که مشخص می‌کند Response برای چه Originی قابل Share شدن است.

آیا Access-Control-Allow-Origin: * خطرناک است؟

همیشه نه. برای API کاملاً Public و بدون Credential می‌تواند مناسب باشد. اما برای Private API یا اطلاعات حساس توصیه نمی‌شود.

آیا * همراه Access-Control-Allow-Credentials کار می‌کند؟

برای Credentialed CORS، Browser نیازمند Origin صریح است و Wildcard * در Access-Control-Allow-Origin قابل استفاده نیست.

Access-Control-Allow-Credentials چیست؟

Headerی است که مشخص می‌کند Response Cross-Origin می‌تواند در Requestهایی که Credential دارند در اختیار Client مجاز قرار گیرد. تنها مقدار معتبر آن true است؛ اگر Credential لازم نیست Header را حذف کنید.

Preflight چیست؟

Request اولیه با Method OPTIONS است که Browser برای برخی Cross-Origin Requestها ارسال می‌کند تا Methodها و Headerهای موردنیاز را با CORS Policy Server بررسی کند.

آیا تمام Requestهای CORS Preflight دارند؟

خیر. بعضی Simple Requestها بدون Preflight ارسال می‌شوند. بنابراین Server نباید Preflight را جایگزین Access Control واقعی کند.

Vary: Origin چیست؟

هنگامی که Response CORS براساس Request Origin به‌صورت Dynamic تغییر می‌کند، Vary: Origin به Cache اعلام می‌کند Responseها ممکن است برای Originهای مختلف متفاوت باشند.

آیا CORS جلوی CSRF را می‌گیرد؟

به‌تنهایی خیر. CORS و CSRF کنترل‌های متفاوتی هستند و APIهای حساس باید CSRF Protection متناسب با معماری داشته باشند.

CORS و CSP چه تفاوتی دارند؟

CORS مشخص می‌کند Resource Server برای چه Originهایی قابل Share است. CSP بیشتر تعیین می‌کند Page چه Resourceهایی را می‌تواند Load یا Execute کند.

آیا CORS جلوی XSS را می‌گیرد؟

خیر. XSS Vulnerability باید با Secure Coding، Output Encoding، CSP و سایر کنترل‌های مربوطه مدیریت شود.

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

زیرا Postman تحت Same-Origin Policy مرورگر نیست. CORS عمدتاً توسط Browser enforce می‌شود.

آیا می‌توان CORS را از JavaScript Frontend حل کرد؟

خیر. Server مقصد باید Response Header مناسب CORS را ارسال کند.

Origin null چیست؟

برخی Opaque Originها مانند بعضی Sandboxed Documentها یا Contextهای خاص به شکل null Serialize می‌شوند. MDN توصیه می‌کند Access-Control-Allow-Origin: null استفاده نشود.

آیا Origin Header قابل اعتماد است؟

برای اجرای CORS در Browser بخشی از Protocol است، اما نباید به‌عنوان Credential یا تنها Access Control Server استفاده شود؛ Clientهای غیرمرورگری می‌توانند خارج از Constraintهای Browser عمل کنند.

آیا باید همه Subdomainها را در CORS Allow کرد؟

فقط اگر واقعاً همه Trusted هستند. Subdomain Takeover یا ضعف یک Subdomain می‌تواند CORS Trust را به خطر بیندازد.

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

WordPress برای REST API تابع rest_send_cors_headers() دارد که CORS Headerهای REST را ارسال می‌کند. رفتار پیش‌فرض و نیاز Integrationها باید قبل از Override بررسی شوند.

آیا WordPress REST API Origin را برای CSRF بررسی می‌کند؟

مستندات WordPress توضیح می‌دهند REST API برای Authentication مبتنی بر Cookie به Nonce متکی است و محدود نکردن پیش‌فرض Origin یک تصمیم طراحی است. در صورت نیاز می‌توان Headerهای سخت‌گیرانه‌تری پیاده‌سازی کرد.

امن‌ترین CORS Configuration چیست؟

تنظیمی که حداقل Origin، Route، Method، Header و Credential موردنیاز Application را Allow کند. هیچ Configuration واحدی برای همه سایت‌ها وجود ندارد.

جمع‌بندی

CORS یکی از مهم‌ترین مکانیزم‌های امنیتی Browser برای مدیریت ارتباط Cross-Origin است، اما هدف و محدودیت‌های آن باید به‌درستی درک شوند.

Cross-Origin Resource Sharing به Server اجازه می‌دهد مشخص کند JavaScript اجراشده در چه Originهایی اجازه دارد Responseهای Resource آن را بخواند.

این قابلیت برای Web Application مدرن ضروری است.

Front-end جدا از API، Headless CMS، SaaS Dashboard و بسیاری از Architectureهای امروزی بدون Cross-Origin Communication قابل استفاده نیستند.

اما هر Originی که به Allowlist CORS اضافه می‌شود بخشی از Trust Boundary Application می‌شود.

به همین دلیل Policy نباید براساس ساده‌ترین راه حذف Error Browser طراحی شود.

قرار دادن:

Access-Control-Allow-Origin: *

روی تمام Domain ممکن است Error Developer را سریع حل کند، اما الزاماً Security Design مناسبی نیست.

اول باید مشخص شود Data Public است یا Private.

اگر Resource عمومی و بدون Credential است، Wildcard می‌تواند انتخاب درستی باشد.

اما اگر Response User-specific یا حساس است، Originهای مجاز باید به‌صورت دقیق تعریف شوند.

در Credentialed CORS، این موضوع اهمیت بیشتری پیدا می‌کند.

Origin باید صریح باشد و Access-Control-Allow-Credentials: true فقط زمانی فعال شود که Cross-Origin Credential واقعاً بخشی از Requirement محصول باشد.

یکی از خطرناک‌ترین Anti-patternها Reflect کردن Origin Request بدون Validation است.

Allowlist باید Origin را با Scheme، Hostname و Port موردانتظار مقایسه کند.

Substring Matching، Regexهای بسیار باز، Wildcard Subdomain و اعتماد به null همگی نیازمند بازبینی امنیتی هستند.

همچنین اگر Origin به‌صورت Dynamic در Response برگردانده می‌شود، Cache باید با:

Vary: Origin

از تفاوت Responseها آگاه شود.

نکته مهم‌تر این است که CORS نباید با Authentication، Authorization یا CSRF Protection اشتباه گرفته شود.

CORS عمدتاً Browser را محدود می‌کند.

API باید فرض کند Clientهای غیرمرورگری نیز می‌توانند مستقیماً به Endpoint آن متصل شوند.

بنابراین User Identity، Object Ownership، Role، Permission و Business Rules همیشه باید در Server بررسی شوند.

حتی Origin رسمی Frontend نباید از Authorization معاف شود.

در سایت‌های WordPress نیز وضعیت مشابه است.

WordPress REST API رفتار CORS مشخصی دارد و قبل از اضافه کردن Headerهای سفارشی باید Behavior Core، Pluginها، Headless Frontend، CDN و Reverse Proxy بررسی شوند.

در نهایت، سؤال صحیح برای طراحی CORS این نیست:

«چطور خطای CORS مرورگر را حذف کنیم؟»

سؤال صحیح این است:

«کدام Origin، برای کدام Resource، با کدام Method و Credential، واقعاً باید اجازه داشته باشد Response این API را از داخل Browser بخواند؟»

هرچه پاسخ این سؤال دقیق‌تر، محدودتر و قابل Auditتر باشد، CORS Policy شما نیز امن‌تر خواهد بود.

مطالب مرتبط