فرض کنید API شما روی این دامنه قرار دارد:
https://api.example.com
اما Frontend شما روی دامنه دیگری اجرا میشود:
https://app.example.com
یا حتی:
https://frontend.example.com
وقتی JavaScript از Frontend به API درخواست ارسال میکند، مرورگر باید بررسی کند که آیا این درخواست Cross-Origin مجاز است یا خیر.
اینجاست که مفهوم CORS وارد میشود.
CORS مخفف:
Cross-Origin Resource Sharing
است و مجموعهای از قوانین و HTTP Headerهاست که به مرورگر اعلام میکند یک Origin مشخص اجازه دارد منابع یا APIهای Origin دیگر را درخواست کند یا خیر.
در این مقاله ابتدا مفهوم Origin را بررسی میکنیم، سپس Preflight Request، درخواستهای OPTIONS، Headerهای CORS، Credentials، Cookieها و در نهایت پیادهسازی امن CORS در PHP را با مثالهای عملی یاد میگیریم.
# CORS چیست؟
CORS مکانیزمی در مرورگر است که اجازه میدهد یک Web Page بتواند در شرایط مشخص به منابعی از Origin دیگر دسترسی داشته باشد.
برای مثال:
Frontend
https://app.example.com
به:
API
https://api.example.com
درخواست ارسال میکند.
این دو Origin متفاوت هستند، بنابراین Browser باید سیاست CORS را بررسی کند.
# Origin چیست؟
برای درک CORS ابتدا باید مفهوم Origin را بدانیم.
Origin از سه بخش اصلی تشکیل میشود:
Scheme
Host
Port
مثلاً:
https://example.com:443
شامل:
Scheme → https
Host → example.com
Port → 443
است.
اگر هرکدام از این موارد متفاوت باشد، ممکن است Origin متفاوت باشد.
# چند مثال از Origin
این دو URL:
https://example.com
https://example.com/products
Origin یکسانی دارند.
چون مسیر URL:
/products
در Origin نقشی ندارد.
اما:
https://example.com
http://example.com
Origin متفاوت دارند.
چون Scheme متفاوت است:
https
http
# Port نیز اهمیت دارد
مثلاً:
https://example.com:443
https://example.com:8443
Origin یکسانی ندارند، چون Port متفاوت است.
بنابراین CORS فقط به Domain نگاه نمیکند.
# Subdomainها چه میشوند؟
این دو:
https://app.example.com
https://api.example.com
Origin متفاوت دارند.
حتی اگر هر دو متعلق به یک Domain اصلی باشند.
پس:
example.com
app.example.com
api.example.com
از نظر CORS لزوماً یک Origin محسوب نمیشوند.
# Same-Origin Policy چیست؟
مرورگرها یک سیاست امنیتی مهم به نام Same-Origin Policy دارند.
هدف این سیاست این است که یک سایت نتواند بدون محدودیت به منابع حساس Originهای دیگر دسترسی پیدا کند.
برای مثال فرض کنید کاربر در یک سایت وارد حساب خود شده است:
https://bank.example
نباید هر سایت دیگری بتواند آزادانه اطلاعات حساس آن سایت را از Browser دریافت کند.
CORS مکانیزمی است که در شرایط مشخص امکان دسترسی Cross-Origin را کنترل میکند.
# CORS امنیت سمت Server است یا Browser؟
CORS بیشتر یک مکانیزم اعمالشده توسط Browser است.
Server با ارسال Headerهایی مانند:
Access-Control-Allow-Origin
اعلام میکند چه Originهایی مجاز هستند.
سپس Browser تصمیم میگیرد آیا JavaScript آن Origin اجازه دسترسی به Response را دارد یا خیر.
# یک مثال ساده
فرض کنید Frontend:
https://app.example.com
است.
API:
https://api.example.com/users
اگر API این Header را ارسال کند:
Access-Control-Allow-Origin: https://app.example.com
Browser میتواند اجازه دسترسی Frontend به Response را بدهد.
# تنظیم CORS در PHP
در سادهترین حالت:
<?php
header(
'Access-Control-Allow-Origin: https://app.example.com'
);
header(
'Content-Type: application/json'
);
echo json_encode([
'message' => 'Hello'
]);
اینجا فقط Origin مشخصشده مجاز است.
# Access-Control-Allow-Origin چیست؟
مهمترین Header در CORS:
Access-Control-Allow-Origin
است.
مثلاً:
header(
'Access-Control-Allow-Origin: https://app.example.com'
);
یعنی:
https://app.example.com
به عنوان Origin مجاز تعریف شده است.
# استفاده از *
ممکن است نمونه زیر را ببینید:
header(
'Access-Control-Allow-Origin: *'
);
این یعنی API از نظر CORS برای Originهای مختلف قابل دسترسی است.
اما این گزینه برای APIهای عمومی ممکن است مناسب باشد و برای APIهای خصوصی یا دارای Credential معمولاً انتخاب مناسبی نیست.
باید توجه کنیم:
*
به معنی «همه چیز کاملاً امن است» نیست.
# CORS و Credentials
یکی از مهمترین بخشهای CORS زمانی است که درخواست شامل Credential باشد.
Credential میتواند شامل مواردی مانند:
- Cookie
- بعضی Authentication Credentialها
- اطلاعات احراز هویت مرورگر
باشد.
در JavaScript مثلاً:
fetch('https://api.example.com/profile', {
credentials: 'include'
});
در این حالت Browser درخواست را با Credentialهای مربوطه ارسال میکند، مشروط به اینکه شرایط امنیتی مربوطه برقرار باشد.
# Access-Control-Allow-Credentials
Server میتواند اعلام کند Credentialهای Cross-Origin مجاز هستند:
header(
'Access-Control-Allow-Credentials: true'
);
اما یک نکته بسیار مهم وجود دارد.
نباید این ساختار را به شکل زیر ترکیب کنیم:
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
برای درخواستهای Credentialed، استفاده از * به عنوان Allow-Origin مناسب نیست و Browser آن را برای این سناریو نمیپذیرد.
به جای آن باید Origin مشخص را برگردانیم.
# روش صحیح برای Credentialed CORS
مثلاً:
header(
'Access-Control-Allow-Origin: https://app.example.com'
);
header(
'Access-Control-Allow-Credentials: true'
);
این روش بسیار دقیقتر است.
# مشکل استفاده مستقیم از Origin کاربر
یکی از اشتباهات خطرناک این است:
$origin = $_SERVER['HTTP_ORIGIN'];
header(
"Access-Control-Allow-Origin: $origin"
);
این کد عملاً میگوید:
> هر Originای که درخواست ارسال کرد، مجاز است.
اگر API خصوصی باشد، این میتواند سیاست CORS را بیاثر کند.
باید Origin را با یک لیست مجاز مقایسه کنیم.
# Allowlist برای Originها
مثلاً:
$allowedOrigins = [
'https://app.example.com',
'https://admin.example.com',
];
سپس:
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
if (in_array($origin, $allowedOrigins, true)) {
header(
"Access-Control-Allow-Origin: $origin"
);
header(
'Access-Control-Allow-Credentials: true'
);
}
این روش بهتر است چون فقط Originهای مشخص اجازه دسترسی دارند.
# Vary: Origin
اگر Response شما بر اساس Origin تغییر میکند، بهتر است Cache نیز از این موضوع مطلع باشد.
میتوانیم:
header('Vary: Origin');
را اضافه کنیم.
این Header به Cacheها اعلام میکند Response برای Originهای مختلف ممکن است متفاوت باشد.
# Preflight Request چیست؟
یکی از مهمترین مفاهیم CORS، Preflight است.
گاهی Browser قبل از ارسال Request اصلی ابتدا یک Request از نوع:
OPTIONS
ارسال میکند.
مثلاً:
Browser
↓
OPTIONS /api/users
↓
Server
Server باید اعلام کند آیا درخواست اصلی مجاز است یا خیر.
# چرا Preflight ارسال میشود؟
همه Cross-Origin Requestها Preflight ندارند.
Browser در شرایط مشخصی درخواست را قبل از ارسال اصلی بررسی میکند.
برای مثال درخواستهایی با:
- Methodهای خاص
- Headerهای خاص
- Content-Typeهای خاص
ممکن است نیازمند Preflight باشند.
# یک مثال Preflight
Browser ممکن است چنین چیزی ارسال کند:
OPTIONS /api/users HTTP/1.1
Origin: https://app.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Authorization, Content-Type
Server باید پاسخ مناسب بدهد.
مثلاً:
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Authorization, Content-Type
# Access-Control-Allow-Methods
این Header مشخص میکند چه HTTP Methodهایی مجاز هستند.
مثلاً:
header(
'Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS'
);
نباید بدون نیاز واقعی تمام Methodها را باز کنیم.
اگر API فقط:
GET
POST
نیاز دارد، میتوان همانها را مجاز کرد.
# Access-Control-Allow-Headers
اگر Frontend Headerهای خاصی ارسال کند، Server باید در صورت نیاز آنها را در CORS مجاز کند.
مثلاً:
header(
'Access-Control-Allow-Headers: '
. 'Content-Type, Authorization, X-CSRF-Token'
);
Headerهای واقعی باید بر اساس نیاز API مشخص شوند.
# مدیریت OPTIONS در PHP
یک API ساده میتواند چنین ساختاری داشته باشد:
<?php
$allowedOrigins = [
'https://app.example.com',
];
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
if (in_array($origin, $allowedOrigins, true)) {
header(
"Access-Control-Allow-Origin: $origin"
);
header('Vary: Origin');
header(
'Access-Control-Allow-Methods: GET, POST, OPTIONS'
);
header(
'Access-Control-Allow-Headers: '
. 'Content-Type, Authorization'
);
}
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(204);
exit;
}
در اینجا اگر Browser درخواست Preflight ارسال کند، Server پاسخ مناسب میدهد.
# چرا 204 برای OPTIONS مناسب است؟
در بسیاری از APIها برای Preflight میتوان پاسخ:
204 No Content
برگرداند.
مثلاً:
http_response_code(204);
exit;
نیازی به Body برای Preflight وجود ندارد.
# یک API کاملتر در PHP
مثلاً:
<?php
header('Content-Type: application/json');
$allowedOrigins = [
'https://app.example.com',
'https://admin.example.com',
];
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
if (in_array($origin, $allowedOrigins, true)) {
header(
"Access-Control-Allow-Origin: $origin"
);
header('Vary: Origin');
header(
'Access-Control-Allow-Credentials: true'
);
header(
'Access-Control-Allow-Methods: '
. 'GET, POST, PUT, DELETE, OPTIONS'
);
header(
'Access-Control-Allow-Headers: '
. 'Content-Type, Authorization'
);
}
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(204);
exit;
}
echo json_encode([
'success' => true,
]);
این ساختار برای آموزش مناسب است، اما در پروژه واقعی باید CORS در یک Middleware یا لایه متمرکز قرار گیرد.
# CORS و Authorization Header
فرض کنیم Frontend این درخواست را ارسال کند:
fetch('https://api.example.com/profile', {
headers: {
'Authorization': 'Bearer TOKEN'
}
});
چون Header سفارشی مانند Authorization استفاده شده است، ممکن است Browser ابتدا Preflight ارسال کند.
در Server باید در صورت نیاز:
header(
'Access-Control-Allow-Headers: Authorization'
);
را مجاز کنیم.
# CORS و Content-Type
فرض کنیم درخواست:
fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Saeed'
})
});
باشد.
در بسیاری از چنین سناریوها Browser Preflight ارسال میکند.
Server باید در صورت نیاز Content-Type را در:
Access-Control-Allow-Headers
اعلام کند.
# Simple Request چیست؟
برخی Cross-Origin Requestها میتوانند بدون Preflight ارسال شوند و در دستهای قرار میگیرند که معمولاً به آنها Simple Request گفته میشود.
اما حتی در این حالت نیز CORS روی دسترسی JavaScript به Response نقش دارد.
بنابراین نباید تصور کنیم:
No Preflight
=
No CORS
# آیا CORS جلوی ارسال Request را میگیرد؟
این موضوع به نوع Request و مرحلهای که Browser در آن قرار دارد بستگی دارد.
نکته مهم این است که CORS در درجه اول روی دسترسی Browser به Response و در درخواستهای Preflighted روی اجازه ارسال Request اصلی تأثیر دارد.
بنابراین CORS را نباید یک Firewall سمت Server تصور کنیم.
اگر Endpoint شما حساس است، همچنان باید Authentication و Authorization را در Server بررسی کنید.
# CORS جایگزین Authentication نیست
این اشتباه بسیار رایج است:
CORS فعال است
پس API امن است.
خیر.
CORS مشخص میکند Browser از چه Originهایی اجازه دسترسی Cross-Origin دارد.
اما Server همچنان باید بررسی کند:
User
Authentication
Authorization
Permission
Resource Ownership
مثلاً:
if (!$user->can('view-profile')) {
http_response_code(403);
exit;
}
CORS جایگزین Authorization نیست.
# CORS و CSRF چه تفاوتی دارند؟
این دو مفهوم بسیار با هم اشتباه گرفته میشوند.
CORS
مکانیزم کنترل دسترسی Cross-Origin در Browser است.
CSRF
حملهای است که در آن مهاجم تلاش میکند Browser قربانی را وادار کند درخواست ناخواستهای به سایت مورد اعتماد ارسال کند.
به طور خلاصه:
CORS
→ Cross-Origin Access Control
CSRF
→ جلوگیری از درخواستهای ناخواسته
این دو مکمل یکدیگر هستند و جایگزین هم نیستند.
# CORS و Cookie
اگر API از Cookie برای Authentication استفاده کند، موضوع پیچیدهتر میشود.
Frontend ممکن است:
fetch('https://api.example.com/profile', {
credentials: 'include'
});
استفاده کند.
در این حالت Server باید CORS مناسب داشته باشد.
مثلاً:
header(
'Access-Control-Allow-Origin: https://app.example.com'
);
header(
'Access-Control-Allow-Credentials: true'
);
و Cookie نیز باید با تنظیمات مناسب مانند:
Secure
HttpOnly
SameSite
پیکربندی شود.
# SameSite و CORS
SameSite و CORS دو چیز متفاوت هستند.
CORS تعیین میکند Browser اجازه دسترسی Cross-Origin به Response را بدهد یا نه.
SameSite روی نحوه ارسال Cookie در شرایط Cross-Site اثر میگذارد.
بنابراین در سیستمهایی که Authentication با Cookie انجام میشود باید هر دو موضوع را جداگانه بررسی کنیم.
# چرا Access-Control-Allow-Origin: * برای API خصوصی مناسب نیست؟
فرض کنید API:
https://api.example.com/account
اطلاعات خصوصی کاربر را برمیگرداند.
اگر بنویسیم:
header(
'Access-Control-Allow-Origin: *'
);
Originهای بسیار زیادی از نظر CORS مجاز میشوند.
برای API عمومی ممکن است این رفتار کاملاً عمدی باشد.
اما برای API خصوصی بهتر است Allowlist داشته باشیم.
# CORS داینامیک امن
روش مناسبتر:
$allowedOrigins = [
'https://app.example.com',
'https://admin.example.com',
];
$origin = $_SERVER['HTTP_ORIGIN'] ?? null;
if (
$origin !== null &&
in_array($origin, $allowedOrigins, true)
) {
header(
"Access-Control-Allow-Origin: $origin"
);
header('Vary: Origin');
}
در این حالت Origin کاربر فقط زمانی در Response قرار میگیرد که قبلاً در Allowlist باشد.
# این روش خطرناک است
کد زیر را استفاده نکنید:
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
header(
"Access-Control-Allow-Origin: $origin"
);
چون هیچ Validation واقعی انجام نشده است.
# استفاده از Regex برای Origin
گاهی میخواهیم چند Subdomain را مجاز کنیم.
مثلاً:
https://app.example.com
https://admin.example.com
https://panel.example.com
بهتر است Allowlist مشخص داشته باشیم.
اگر واقعاً نیاز به Pattern داریم، باید Parsing و Validation دقیق انجام شود و نباید Regex ساده و بیشازحد باز بنویسیم که Originهای ناخواسته را نیز قبول کند.
# CORS و Wildcard Subdomain
این تصور اشتباه است:
*.example.com
را مستقیماً به عنوان:
Access-Control-Allow-Origin
قرار دهیم و انتظار داشته باشیم Browser آن را به عنوان Wildcard Subdomain تفسیر کند.
برای Dynamic Origin باید خودمان Origin را بررسی کنیم و فقط مقدار معتبر را برگردانیم.
# Access-Control-Max-Age
میتوان مدت زمانی را که Browser نتیجه Preflight را Cache میکند مشخص کرد.
مثلاً:
header(
'Access-Control-Max-Age: 600'
);
یعنی Browser میتواند نتیجه Preflight را برای مدت مشخصی Cache کند.
مقدار مناسب به نیاز پروژه بستگی دارد.
# CORS و Security Headers
در مقاله قبلی Security Headers را بررسی کردیم.
CORS نیز از Headerهای HTTP استفاده میکند، اما هدف آن متفاوت است.
مثلاً:
Content-Security-Policy
→ کنترل منابع صفحه
Strict-Transport-Security
→ اجبار HTTPS
CORS
→ کنترل دسترسی Cross-Origin
بنابراین همه Security Headerها یک کار انجام نمیدهند.
# CORS در REST API
در APIهای REST معمولاً بهتر است CORS در یک Middleware یا لایه مشترک قرار گیرد.
ساختار:
Request
↓
CORS Middleware
↓
Authentication
↓
Authorization
↓
Controller
↓
Response
این کار باعث میشود CORS در تمام Endpointها به صورت یکسان مدیریت شود.
# ساخت کلاس CORS در PHP
برای پروژه PHP خام میتوانیم یک کلاس ساده ایجاد کنیم:
<?php
class Cors
{
public function __construct(
private array $allowedOrigins
) {
}
public function handle(): void
{
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
if (
in_array(
$origin,
$this->allowedOrigins,
true
)
) {
header(
"Access-Control-Allow-Origin: $origin"
);
header('Vary: Origin');
header(
'Access-Control-Allow-Credentials: true'
);
header(
'Access-Control-Allow-Methods: '
. 'GET, POST, PUT, DELETE, OPTIONS'
);
header(
'Access-Control-Allow-Headers: '
. 'Content-Type, Authorization'
);
}
if (
$_SERVER['REQUEST_METHOD'] === 'OPTIONS'
) {
http_response_code(204);
exit;
}
}
}
استفاده:
$cors = new Cors([
'https://app.example.com',
'https://admin.example.com',
]);
$cors->handle();
این ساختار باعث میشود منطق CORS از Controllerها جدا شود.
# آیا Origin نامعتبر باید 403 دریافت کند؟
لزومی ندارد همیشه همین رفتار را داشته باشیم.
اگر Origin در Allowlist نباشد، میتوانیم Headerهای CORS را اضافه نکنیم.
در نتیجه Browser اجازه دسترسی JavaScript به Response را نمیدهد.
اما همچنان باید Authentication و Authorization مستقل از CORS اجرا شوند.
CORS نباید جایگزین کنترل دسترسی سمت Server شود.
# CORS و Error Response
یک نکته مهم این است که CORS فقط برای Response موفق نیست.
اگر API خطا برگرداند:
401
403
404
429
500
در معماری مناسب باید رفتار CORS نیز برای Responseهای موردنیاز مشخص باشد.
به همین دلیل تنظیم CORS در یک Middleware مرکزی معمولاً بهتر از قرار دادن Headerها داخل هر Controller است.
# CORS و 401 / 403
فرض کنید کاربر احراز هویت نشده است:
HTTP/1.1 401 Unauthorized
حتی این Response نیز ممکن است به Headerهای CORS مناسب نیاز داشته باشد تا Frontend بتواند آن را به شکل صحیح دریافت و مدیریت کند.
# CORS و Rate Limiting
CORS و Rate Limiting نیز نقش متفاوتی دارند.
CORS
→ چه Originهایی مجازند؟
Rate Limiting
→ چند درخواست مجاز است؟
یک API امن میتواند هر دو را داشته باشد:
Request
↓
CORS
↓
Rate Limiting
↓
Authentication
↓
Authorization
↓
Controller
# CORS و API Key
اگر API با API Key کار میکند:
Authorization: Bearer ...
یا:
X-API-Key: ...
باشد، Header موردنظر باید در صورت نیاز در:
Access-Control-Allow-Headers
قرار گیرد.
اما CORS به هیچ عنوان اعتبار API Key را تأیید نمیکند.
این کار وظیفه Authentication سمت Server است.
# اشتباهات رایج در CORS
۱. استفاده بیدلیل از *
برای API خصوصی مناسب نیست.
۲. بازگرداندن مستقیم Origin
این کد خطرناک است:
header(
'Access-Control-Allow-Origin: '
. $_SERVER['HTTP_ORIGIN']
);
باید Allowlist داشته باشیم.
۳. استفاده از * همراه Credential
برای Credentialed CORS باید Origin مشخص باشد.
۴. فراموش کردن OPTIONS
اگر Request نیاز به Preflight داشته باشد و Server آن را مدیریت نکند، API ممکن است از سمت Browser کار نکند.
۵. تصور اینکه CORS جای Authentication است
CORS مجوز دسترسی کاربر به داده را تعیین نمیکند.
۶. تصور اینکه CORS جلوی همه درخواستهای مخرب را میگیرد
CORS مکانیزم امنیتی Browser است و مهاجم میتواند مستقیماً به API درخواست ارسال کند.
بنابراین Server همچنان باید Authentication، Authorization، Validation و Rate Limiting داشته باشد.
۷. باز کردن بیش از حد Headerها
اگر API فقط به:
Content-Type
Authorization
نیاز دارد، نیازی نیست تعداد زیادی Header را بدون دلیل مجاز کنیم.
# یک نمونه پیشنهادی برای API واقعی
ساختار پایه:
<?php
$allowedOrigins = [
'https://app.example.com',
'https://admin.example.com',
];
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
if (
in_array(
$origin,
$allowedOrigins,
true
)
) {
header(
"Access-Control-Allow-Origin: $origin"
);
header('Vary: Origin');
header(
'Access-Control-Allow-Credentials: true'
);
header(
'Access-Control-Allow-Methods: '
. 'GET, POST, PUT, DELETE, OPTIONS'
);
header(
'Access-Control-Allow-Headers: '
. 'Content-Type, Authorization'
);
header(
'Access-Control-Max-Age: 600'
);
}
if (
$_SERVER['REQUEST_METHOD'] === 'OPTIONS'
) {
http_response_code(204);
exit;
}
header(
'Content-Type: application/json'
);
echo json_encode([
'success' => true,
]);
این نمونه یک نقطه شروع آموزشی است و Policy واقعی باید بر اساس معماری API تنظیم شود.
# چکلیست CORS در PHP
قبل از انتشار API این موارد را بررسی کنید:
- [ ] Originهای مجاز مشخص شدهاند.
- [ ] Origin کاربر بدون Validation بازتاب داده نمیشود.
- [ ] برای API خصوصی از
*استفاده نشده است. - [ ] Credentialed Requestها به صورت صحیح تنظیم شدهاند.
- [ ]
Access-Control-Allow-Credentialsفقط در صورت نیاز فعال است. - [ ] Preflight Request مدیریت میشود.
- [ ]
OPTIONSبه درستی پاسخ داده میشود. - [ ] Methodهای مجاز مشخص هستند.
- [ ] Headerهای مجاز مشخص هستند.
- [ ]
Vary: Originدر Dynamic CORS در نظر گرفته شده است. - [ ] CORS جایگزین Authentication نشده است.
- [ ] Authorization مستقل از CORS انجام میشود.
- [ ] Rate Limiting فعال است.
- [ ] CSRF در معماری Cookie-based به درستی بررسی شده است.
- [ ] Cookieها
Secure،HttpOnlyوSameSiteمناسب دارند. - [ ] Responseهای خطا نیز در طراحی CORS در نظر گرفته شدهاند.
- [ ] CORS در Middleware یا لایه متمرکز مدیریت میشود.
- [ ] Originهای غیرمجاز تست شدهاند.
# پرسشهای متداول
CORS در PHP چیست؟
CORS مکانیزمی است که به Browser اجازه میدهد بر اساس سیاستهای تعریفشده، دسترسی یک Origin به منابع Origin دیگر را کنترل کند.
آیا CORS فقط در PHP وجود دارد؟
خیر. CORS یک مکانیزم وب و Browser است و Serverهای مختلف از جمله PHP، Node.js، Python و Java میتوانند Headerهای مربوط به آن را ارسال کنند.
آیا CORS از API در برابر Hacker محافظت میکند؟
به تنهایی خیر. CORS عمدتاً رفتار Browser را کنترل میکند. API همچنان به Authentication، Authorization، Validation و Rate Limiting نیاز دارد.
Preflight چیست؟
Preflight یک درخواست OPTIONS است که Browser در شرایط مشخص قبل از درخواست Cross-Origin اصلی ارسال میکند تا بررسی کند آیا درخواست مجاز است یا خیر.
آیا Access-Control-Allow-Origin: * امن است؟
برای API عمومی ممکن است عمداً استفاده شود، اما برای API خصوصی و مخصوصاً سناریوهای Credentialed معمولاً نباید بدون بررسی استفاده شود.
آیا CORS جایگزین CSRF است؟
خیر. CORS و CSRF دو مفهوم متفاوت هستند و در سیستمهای Cookie-based ممکن است هر دو مورد نیاز باشند.
چرا CORS در Postman کار میکند ولی در Browser خطا میدهد؟
Postman مانند Browser همان محدودیتهای CORS را اعمال نمیکند. CORS عمدتاً یک سیاست اعمالشده توسط Browser است. بنابراین ممکن است API در Postman پاسخ دهد اما JavaScript مرورگر به دلیل Policy CORS نتواند Response را بخواند.
# جمعبندی
CORS یکی از مفاهیم مهم در توسعه REST API و برنامههای مدرن وب است.
وقتی Frontend و API روی Originهای متفاوت قرار دارند، Browser باید بداند آیا دسترسی Cross-Origin مجاز است یا خیر.
مهمترین Headerهایی که در این زمینه با آنها کار کردیم عبارتاند از:
Access-Control-Allow-Origin
Access-Control-Allow-Methods
Access-Control-Allow-Headers
Access-Control-Allow-Credentials
Access-Control-Max-Age
Vary: Origin
همچنین با مفاهیم مهمی مانند:
Origin
Same-Origin Policy
Preflight
OPTIONS
Credentials
Cookie
آشنا شدیم.
یکی از مهمترین نکات امنیتی این است که هرگز نباید صرفاً برای رفع خطای CORS، Origin دریافتشده از کاربر را بدون بررسی مستقیماً در Access-Control-Allow-Origin قرار دهیم.
همچنین CORS نباید با Authentication، Authorization، CSRF یا Rate Limiting اشتباه گرفته شود.
یک API امن معمولاً به مجموعهای از این لایهها نیاز دارد:
HTTPS
↓
CORS
↓
Rate Limiting
↓
Authentication
↓
Authorization
↓
Input Validation
↓
Business Logic
در نتیجه CORS را باید بخشی از معماری API بدانیم، نه یک تنظیم ساده برای رفع خطای Browser.
مقاله پیشنهادی بعدی
بعد از CORS، ادامه منطقی این کلاستر میتواند API Security در PHP باشد؛ در آن مقاله میتوانیم Authentication، Authorization، API Key، Bearer Token، JWT، Rate Limiting، CORS، Validation، HTTPS، Error Handling و Logging را در قالب یک معماری کامل و امن برای APIهای PHP کنار هم قرار دهیم.





