CORS چیست و دقیقاً چه کاری میکند؟
CORS مخفف Cross-Origin Resource Sharing («اشتراک منابع میانمبدأ») است: هدرهایی که با آنها سرور به مرورگر اعلام میکند اسکریپتِ کدام مبدأهای دیگری اجازه دارد پاسخ این منبع را بخواند.
نقطهی شروع، سیاست مبدأ یکسان (Same-Origin Policy) است: مرورگر نمیگذارد جاوااسکریپتِ یک مبدأ، پاسخ درخواستی به مبدأ دیگر را بخواند. «مبدأ» یعنی ترکیب سهتایی طرح، میزبان و پورت — https://app.example.com و https://api.example.com دو مبدأ جدا هستند و همین، تیمها را به سراغ CORS میفرستد.
دو قاعدهی سخت که مدام نقض میشوند:
Access-Control-Allow-Originفقط یک مبدأ یا کاراکتر*میپذیرد؛ فهرست جداشده با کاما معتبر نیست. به همین دلیل تیمها به «بازتاب مبدأ» پناه میبرند و آسیبپذیری دقیقاً از همینجا متولد میشود.- در پاسخ به درخواستی که اعتبارنامه دارد (کوکی، هدر
Authorization، گواهی کلاینت)، سرور نباید*بفرستد؛ مرورگر چنین پاسخی را رد میکند. همین ممنوعیت برایAccess-Control-Allow-Headers،-Methodsو-Expose-Headersهم برقرار است.
اگر سرور مبدأ را پویا بازتاب میدهد، ارسال Vary: Origin الزامی است؛ وگرنه کش میانی پاسخِ یک مبدأ را به مبدأ دیگری تحویل میدهد.
CORS فقط خواندن پاسخ را کنترل میکند؛ درخواست میانمبدأ حتی وقتی مرورگر پاسخش را پنهان میکند، به سرور رسیده و اثرش را گذاشته است. پس CORS هیچگاه دفاعی در برابر CSRF نیست.
درخواست ساده در برابر درخواست Preflight
درخواست ساده بدون هماهنگی قبلی ارسال میشود و مرورگر صرفاً پاسخ را بر اساس ACAO فیلتر میکند. شرط ساده بودن: متد GET، HEAD یا POST باشد، هدر سفارشی نداشته باشد، و Content-Type یکی از این سه باشد: application/x-www-form-urlencoded، multipart/form-data، text/plain.
Preflight وقتی رخ میدهد که یکی از این شرطها نقض شود — متد PUT یا DELETE، هدری مثل Authorization، یا Content-Type: application/json. مرورگر ابتدا یک OPTIONS میفرستد و تنها با پاسخ موافق، درخواست اصلی را ارسال میکند.
OPTIONS /api/v1/orders HTTP/1.1
Origin: https://app.example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: authorization
HTTP/1.1 204 No Content
Access-Control-Allow-Methods: GET, PUT
Access-Control-Allow-Headers: authorization
Access-Control-Max-Age: 600این تفکیک کاربرد دفاعی دارد: فرم HTML نه هدر سفارشی میفرستد و نه Content-Type دلخواه میگذارد؛ پس الزام یک هدر سفارشی روی API، هر درخواست میانسایتی را وادار به Preflight میکند — لایهای که در تست نفوذ API بررسی میشود.
انواع پیکربندی نادرست، به ترتیب شدت واقعی
| الگو | چرا خطرناک است | شدت معمول |
|---|---|---|
بازتاب مبدأ + Allow-Credentials: true | هر سایتی با کوکی قربانی درخواست میزند و پاسخ را میخواند؛ خواندنِ کاملِ دادهی احرازهویتشده | بالا تا بحرانی |
Allow-Origin: null با اعتبارنامه | مقدار null با یک iframe سندباکسشده تولید میشود؛ مجاز کردنش یعنی مجاز کردن همه | بالا تا بحرانی |
| تطبیق سست مبدأ (regex بدون لنگر، تطبیق پیشوند یا پسوند) | evilexample.com یا example.com.evil.com از فیلتر رد میشوند | بالا |
| اعتماد به همهی زیردامنهها | یک XSS روی هر زیردامنه به کانال استخراج داده از API تبدیل میشود | متوسط تا بالا |
پذیرش مبدأهای http:// | مهاجمِ حاضر در مسیر شبکه روی زیردامنهی بدون TLS، خود را به مبدأ مجاز تبدیل میکند | متوسط |
Allow-Origin: * بدون اعتبارنامه | بهتنهایی معمولاً آسیبپذیری نیست — مگر اندپوینت با IP یا موقعیت شبکه احراز هویت کند، یا سرویس داخلیِ در دسترسِ مرورگر قربانی باشد | اطلاعاتی تا بالا |
سناریوی واقعی
یک سامانهی حسابداری ابری برای پشتیبانی از دامنههای اختصاصی مشتریان، بهجای فهرست سفید، هدر Origin دریافتی را بازتاب میدهد؛ و چون رابط کاربری با کوکی نشست کار میکند، Access-Control-Allow-Credentials: true هم میفرستد. از دید تیم توسعه این «پشتیبانی از چند دامنه» است، نه یک تصمیم امنیتی.
اثبات اثر ساده است: صفحهای روی یک دامنهی آزمایشی، با درخواست میانمبدأ و ارسال اعتبارنامه، اندپوینت /api/v1/me را صدا میزند. اگر حسابدارِ لاگینشده آن را باز کند، مرورگر کوکی نشست را ضمیمه میکند، سرور مبدأ مهاجم را بازتاب میدهد و اسکریپت پاسخ را میخواند: نام، ایمیل، شناسهی سازمان و توکن API.
هیچ آسیبپذیری دیگری لازم نبود — نه XSS، نه ضعف رمزنگاری؛ تنها ورودی مهاجم یک هدر Origin بود.
چگونه در تست نفوذ کشف میشود (WSTG-CLNT-07)
آزمون CORS در راهنمای آزمون امنیت وب OWASP (نسخهی پایدار WSTG v4.2) زیر بخش تست سمت کلاینت با شناسهی WSTG-CLNT-07 قرار دارد. روال حرفهای در پنج گام:
- اندپوینتهای حساسی را انتخاب کنید که دادهی کاربر برمیگردانند، نه صفحهی اصلی.
- مجموعهی ثابتی از مبدأ بفرستید: یک دامنهی بیگانه، مقدار
null، پسوند جعلی (https://target.com.evil.example)، پیشوند جعلی (https://eviltarget.com) و نسخهیhttp://خودِ دامنه. - در پاسخ سه چیز را ببینید:
Access-Control-Allow-Origin،Allow-Credentials: trueوVary: Origin. با ارسال دو مبدأ متفاوت، بازتاب را از فهرست سفید تفکیک کنید. - Preflight را جدا بیازمایید؛ اغلب پاسخ
OPTIONSرا API Gateway میدهد و پاسخGETرا خود برنامه، با سیاستهای متفاوت. - اثر واقعی را بسنجید: بدون
Allow-Credentials، پیش از ثبت یافته بررسی کنید اندپوینت با IP یا موقعیت شبکه احراز هویت میکند یا نه.
شکل یک یافتهی قطعی در گزارش این است:
GET /api/v1/me HTTP/1.1
Host: api.target.example
Origin: https://evil.example
Cookie: session=<victim-session>
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://evil.example
Access-Control-Allow-Credentials: trueابزار اصلی پروکسی رهگیر است: با Burp Suite یا Caido همین مجموعه مبدأ را روی کل تاریخچه بازپخش کنید — و فقط با مجوز کتبی، در چارچوب متدولوژی تست نفوذ.
دو باور غلط رایج و نگاشت به استانداردها
«Access-Control-Allow-Origin: * یعنی آسیبپذیری بحرانی.» معمولاً نه — و ثبت خودکار آن بهعنوان یافتهی بحرانی، نشانهی گزارش ضعیف است. مرورگر حاضر نیست کوکی یا هدر Authorization را به پاسخی با ACAO برابر * تحویل دهد؛ یعنی مهاجم فقط همان دادهای را میخواند که بدون احراز هویت هم در دسترس بود. این تنظیم وقتی یافتهی واقعی میشود که اندپوینت با IP یا موقعیت شبکه احراز هویت کند، یا سرویس داخلیای باشد که مرورگر قربانی از شبکه به آن دسترسی دارد. الگوی واقعاً بحرانی چیز دیگری است: بازتاب مبدأ + Access-Control-Allow-Credentials: true و Origin: null + اعتبارنامه.
«CORS یک قابلیت امنیتی است که از API ما محافظت میکند.» دقیقاً برعکس. CORS سیاست مبدأ یکسان را شل میکند؛ حالت پیشفرضِ امن یعنی «هیچ هدر CORS» و هر هدری که اضافه میکنید یک استثنا روی محافظت پیشفرض مرورگر است. پیکربندی بازِ CORS محافظت را برمیدارد و هرگز محافظت اضافه نمیکند؛ ضمناً جایگزین کنترل دسترسی هم نیست و برای کلاینتهای غیرمرورگری اصلاً وجود ندارد.
نگاشت: این ضعف ذیل A02:2025 پیکربندی نادرست امنیتی مینشیند — دستهای که در نسخهی ۲۰۲۵ از رتبهی پنج به رتبهی دو صعود کرد. نزدیکترین شناسههای CWE، CWE-942 و CWE-346 (خطا در اعتبارسنجی مبدأ) هستند. شدت را با CVSS و بر اساس دادهای که واقعاً قابل خواندن است امتیاز بدهید، نه بر اساس وجود هدر؛ جایگاه این دسته در راهنمای OWASP Top 10 آمده است.
پرسشهای متداول
آیا Access-Control-Allow-Origin: * خطرناک است؟
بهتنهایی معمولاً نه. مرورگر اجازه نمیدهد کوکی یا هدر Authorization همراه پاسخی با ACAO برابر * خوانده شود، پس مهاجم چیزی بیش از دادهی عمومی بهدست نمیآورد. اما اگر آن اندپوینت با آدرس IP یا موقعیت شبکه احراز هویت کند، یا سرویس داخلیای باشد که مرورگر کاربرِ شبکهی داخلی به آن دسترسی دارد، همان تنظیم به یافتهی جدی تبدیل میشود.
آیا CORS از API من محافظت میکند؟
خیر. CORS سیاست مبدأ یکسان را شل میکند، نه اینکه محافظت اضافه کند؛ و فقط روی مرورگر اثر دارد. curl یا هر کلاینت غیرمرورگری هدرهای CORS را نادیده میگیرد. محافظت واقعی از API از احراز هویت، مجوزدهی سمت سرور و محدودسازی نرخ میآید که در تست نفوذ API سنجیده میشوند.
آیا تنظیم درست CORS جلوی CSRF را میگیرد؟
نه. CORS خواندن پاسخ را کنترل میکند، نه رسیدن درخواست را. یک فرم میانسایتی همچنان به سرور میرسد و عملیات را انجام میدهد، حتی اگر مرورگر پاسخ را از مهاجم پنهان کند. دفاع در برابر CSRF همچنان به توکن همزمانساز، تنظیم صریح SameSite و بررسی Origin در سمت سرور نیاز دارد.