CORE · مفاهیم سیاست مرورگر

CORS — اشتراک منابع میان‌مبدأ

Cross-Origin Resource Sharing

CORS سازوکاری است که سیاست مبدأ یکسان مرورگر را به‌شکل کنترل‌شده شل می‌کند؛ پیکربندی نادرست آن — به‌ویژه بازتاب مبدأ همراه با پذیرش اعتبارنامه — به هر سایتی اجازه می‌دهد داده‌ی کاربرِ احرازهویت‌شده‌ی شما را بخواند.

تیم فنی پی‌هانتر

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 فقط خواندن پاسخ را کنترل می‌کند؛ درخواست میان‌مبدأ حتی وقتی مرورگر پاسخش را پنهان می‌کند، به سرور رسیده و اثرش را گذاشته است. پس 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 قرار دارد. روال حرفه‌ای در پنج گام:

  1. اندپوینت‌های حساسی را انتخاب کنید که داده‌ی کاربر برمی‌گردانند، نه صفحه‌ی اصلی.
  2. مجموعه‌ی ثابتی از مبدأ بفرستید: یک دامنه‌ی بیگانه، مقدار null، پسوند جعلی (https://target.com.evil.example)، پیشوند جعلی (https://eviltarget.com) و نسخه‌ی http:// خودِ دامنه.
  3. در پاسخ سه چیز را ببینید: Access-Control-Allow-Origin، Allow-Credentials: true و Vary: Origin. با ارسال دو مبدأ متفاوت، بازتاب را از فهرست سفید تفکیک کنید.
  4. Preflight را جدا بیازمایید؛ اغلب پاسخ OPTIONS را API Gateway می‌دهد و پاسخ GET را خود برنامه، با سیاست‌های متفاوت.
  5. اثر واقعی را بسنجید: بدون 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 در سمت سرور نیاز دارد.

پیشگیری / رفع

به ترتیب اثربخشی:

  1. فهرست سفید سمت سرور با تطبیق کامل رشته. فقط در صورت تطبیق دقیق، همان مقدار را در ACAO بگذارید. از regex پرهیز کنید؛ اگر ناچارید، لنگر ابتدا و انتها را فراموش نکنید.
  2. هرگز مبدأ ورودی را بدون تطبیق بازتاب ندهید و در هر پاسخِ وابسته به مبدأ، Vary: Origin بفرستید.
  3. مقدار null را هرگز مجاز نکنید — از iframe سندباکس‌شده و سند تغییرمسیرداده‌شده تولید می‌شود و معادل «همه» است.
  4. Access-Control-Allow-Credentials: true را فقط جایی روشن کنید که واقعاً لازم است. اگر API با توکن Bearer کار می‌کند و کوکی ندارد، حذف این هدر به‌تنهایی کل کلاس حمله‌ی بازتاب مبدأ را بی‌اثر می‌کند.
  5. زیردامنه‌ها را تک‌تک اضافه کنید، نه با الگوی *.example.com؛ و فقط مبدأهای https:// را بپذیرید.
  6. مجوزدهی را مستقل از CORS اعمال کنید و پس از هر تغییر دوباره آزمون بگیرید — سیاست CORS معمولاً هم‌زمان در وب‌سرور، API Gateway و کد برنامه تعریف شده است.

تست نفوذ وب این را اندپوینت‌به‌اندپوینت می‌سنجد؛ برای بازبینی معماری چنددامنه‌ای پیش از پیاده‌سازی، مشاوره‌ی امنیتی گزینه‌ی ارزان‌تری است.

→ بازگشت به دانشنامه
PUT IT TO THE TEST

امنیت سامانه‌ی شما را
به مهاجمان واگذار نکنید

تیم پی‌هانتر با دیدِ یک مهاجم واقعی، این آسیب‌پذیری‌ها و ده‌ها مورد دیگر را روی دارایی‌های شما می‌سنجد.