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/page1 | https://example.com/page2 | Same-Origin |
https://example.com | http://example.com | Cross-Origin |
https://example.com | https://api.example.com | Cross-Origin |
https://example.com:443 | https://example.com:8443 | Cross-Origin |
https://example.com/a | https://example.com/b | Same-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 را میتوان در چهار مرحله خلاصه کرد.
مرحله اول: 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 نشود.
مدل صحیح
- Origin Request دریافت شود.
- با لیست دقیق Originهای مورداعتماد مقایسه شود.
- تنها در صورت Match کامل در Response Reflect شود.
- در غیر این صورت 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های مشخص داشته باشند.
آیا * همراه Cookie کار میکند؟
در 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 چیست؟
برای 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 |
|---|---|---|
| هدف اصلی | کنترل Cross-Origin Response Sharing | جلوگیری از Request ناخواسته با Session کاربر |
| اجراکننده اصلی | Browser براساس Header Server | Server + Browser Controls |
| Controlهای رایج | ACAO، Credentials، Preflight | CSRF Token، SameSite، Origin Check |
| آیا جای Authorization است؟ | خیر | خیر |
| تمرکز | Read/Interaction Cross-Origin | Forged 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 پنج سؤال بپرسید:
- چه Originهایی Allow هستند؟
- Response چه Dataیی دارد؟
- آیا Credential همراه Request میتواند ارسال شود؟
- آیا User-specific Data برگردانده میشود؟
- اگر 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
شناسایی نیاز
- آیا واقعاً 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 چیست؟
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 شما نیز امنتر خواهد بود.