راهنمای جامع برای درک و پیادهسازی اشتراکگذاری منابع میان-مبدا (CORS) برای ارتباط امن جاوا اسکریپت بین دامنههای مختلف.
پیادهسازی امنیت میان-مبدا: بهترین شیوهها برای ارتباط جاوا اسکریپت
در وب به هم پیوسته امروزی، اپلیکیشنهای جاوا اسکریپت اغلب نیاز دارند با منابعی از مبداهای مختلف (دامنهها، پروتکلها یا پورتهای متفاوت) تعامل داشته باشند. این تعامل توسط سیاست همان-مبدا (Same-Origin Policy) مرورگر کنترل میشود که یک مکانیزم امنیتی حیاتی برای جلوگیری از دسترسی اسکریپتهای مخرب به دادههای حساس در مرزهای دامنهها است. با این حال، ارتباطات قانونی میان-مبدا اغلب ضروری است. اینجاست که اشتراکگذاری منابع میان-مبدا (CORS) وارد عمل میشود. این مقاله یک نمای کلی از CORS، نحوه پیادهسازی آن و بهترین شیوهها برای ارتباط امن میان-مبدا در جاوا اسکریپت ارائه میدهد.
درک سیاست همان-مبدا (Same-Origin Policy)
سیاست همان-مبدا (SOP) یک مفهوم امنیتی اساسی در مرورگرهای وب است. این سیاست، اسکریپتهایی که در یک مبدا اجرا میشوند را از دسترسی به منابع یک مبدا متفاوت محدود میکند. یک مبدا با ترکیبی از پروتکل (مانند HTTP یا HTTPS)، نام دامنه (مانند example.com) و شماره پورت (مانند 80 یا 443) تعریف میشود. دو URL تنها زمانی دارای مبدا یکسان هستند که هر سه جزء آنها دقیقاً مطابقت داشته باشند.
برای مثال:
http://www.example.comوhttp://www.example.com/path: مبدا یکسانhttp://www.example.comوhttps://www.example.com: مبدا متفاوت (پروتکل متفاوت)http://www.example.comوhttp://subdomain.example.com: مبدا متفاوت (دامنه متفاوت)http://www.example.com:80وhttp://www.example.com:8080: مبدا متفاوت (پورت متفاوت)
SOP یک دفاع حیاتی در برابر حملات اسکریپتنویسی میانسایتی (XSS) است، جایی که اسکریپتهای مخرب تزریق شده به یک وبسایت میتوانند دادههای کاربر را سرقت کرده یا اقدامات غیرمجازی را از طرف کاربر انجام دهند.
اشتراکگذاری منابع میان-مبدا (CORS) چیست؟
CORS مکانیزمی است که از هدرهای HTTP استفاده میکند تا به سرورها اجازه دهد مشخص کنند کدام مبداها (دامنهها، طرحها یا پورتها) مجاز به دسترسی به منابع آنها هستند. این مکانیزم اساساً سیاست همان-مبدا را برای درخواستهای خاص میان-مبدا تسهیل میکند و ارتباطات قانونی را امکانپذیر میسازد، در حالی که همچنان در برابر حملات مخرب محافظت میکند.
CORS با افزودن هدرهای جدید HTTP کار میکند که مبداهای مجاز و متدهای (مانند GET, POST, PUT, DELETE) مجاز برای درخواستهای میان-مبدا را مشخص میکنند. هنگامی که یک مرورگر یک درخواست میان-مبدا ارسال میکند، یک هدر Origin به همراه درخواست میفرستد. سرور با هدر Access-Control-Allow-Origin پاسخ میدهد که مبدا(های) مجاز را مشخص میکند. اگر مبدا درخواست با مقدار موجود در هدر Access-Control-Allow-Origin مطابقت داشته باشد (یا اگر مقدار * باشد)، مرورگر به کد جاوا اسکریپت اجازه دسترسی به پاسخ را میدهد.
CORS چگونه کار میکند: یک توضیح دقیق
فرآیند CORS معمولاً شامل دو نوع درخواست است:
- درخواستهای ساده (Simple Requests): اینها درخواستهایی هستند که معیارهای خاصی را برآورده میکنند. اگر درخواستی این شرایط را داشته باشد، مرورگر مستقیماً درخواست را ارسال میکند.
- درخواستهای پیشپرواز (Preflighted Requests): اینها درخواستهای پیچیدهتری هستند که مرورگر را ملزم میکنند ابتدا یک درخواست OPTIONS «پیشپرواز» به سرور ارسال کند تا مشخص شود آیا ارسال درخواست واقعی امن است یا خیر.
۱. درخواستهای ساده
یک درخواست در صورتی «ساده» در نظر گرفته میشود که تمام شرایط زیر را داشته باشد:
- متد آن
GET،HEADیاPOSTباشد. - اگر متد
POSTباشد، هدرContent-Typeیکی از موارد زیر باشد: application/x-www-form-urlencodedmultipart/form-datatext/plain- هیچ هدر سفارشی تنظیم نشده باشد.
مثالی از یک درخواست ساده:
GET /resource HTTP/1.1
Origin: http://www.example.com
مثالی از پاسخ سرور که به مبدا اجازه میدهد:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://www.example.com
Content-Type: application/json
{
"data": "Some data"
}
اگر هدر Access-Control-Allow-Origin وجود داشته باشد و مقدار آن با مبدا درخواست مطابقت داشته باشد یا روی * تنظیم شده باشد، مرورگر به اسکریپت اجازه دسترسی به دادههای پاسخ را میدهد. در غیر این صورت، مرورگر دسترسی به پاسخ را مسدود میکند و یک پیام خطا در کنسول نمایش داده میشود.
۲. درخواستهای پیشپرواز (Preflighted)
یک درخواست در صورتی «پیشپرواز» در نظر گرفته میشود که معیارهای یک درخواست ساده را برآورده نکند. این حالت معمولاً زمانی رخ میدهد که درخواست از یک متد HTTP متفاوت (مانند PUT، DELETE)، هدرهای سفارشی یا Content-Type غیر از مقادیر مجاز استفاده کند.
قبل از ارسال درخواست واقعی، مرورگر ابتدا یک درخواست OPTIONS به سرور ارسال میکند. این درخواست «پیشپرواز» شامل هدرهای زیر است:
Origin: مبدا صفحه درخواستکننده.Access-Control-Request-Method: متد HTTP که در درخواست واقعی استفاده خواهد شد (مانندPUT،DELETE).Access-Control-Request-Headers: لیستی از هدرهای سفارشی که در درخواست واقعی ارسال خواهند شد، که با کاما از هم جدا شدهاند.
مثالی از یک درخواست پیشپرواز:
OPTIONS /resource HTTP/1.1
Origin: http://www.example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header, Content-Type
سرور باید به درخواست OPTIONS با هدرهای زیر پاسخ دهد:
Access-Control-Allow-Origin: مبدایی که مجاز به ارسال درخواست است (یا*برای اجازه دادن به هر مبدایی).Access-Control-Allow-Methods: لیستی از متدهای HTTP مجاز برای درخواستهای میان-مبدا که با کاما از هم جدا شدهاند (مانندGET،POST،PUT،DELETE).Access-Control-Allow-Headers: لیستی از هدرهای سفارشی مجاز برای ارسال در درخواست که با کاما از هم جدا شدهاند.Access-Control-Max-Age: تعداد ثانیههایی که پاسخ پیشپرواز میتواند توسط مرورگر کش شود.
مثالی از پاسخ سرور به یک درخواست پیشپرواز:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://www.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: X-Custom-Header, Content-Type
Access-Control-Max-Age: 86400
اگر پاسخ سرور به درخواست پیشپرواز نشان دهد که درخواست واقعی مجاز است، مرورگر درخواست واقعی را ارسال خواهد کرد. در غیر این صورت، مرورگر درخواست را مسدود کرده و یک پیام خطا نمایش میدهد.
پیادهسازی CORS در سمت سرور
CORS عمدتاً در سمت سرور با تنظیم هدرهای HTTP مناسب در پاسخ پیادهسازی میشود. جزئیات پیادهسازی خاص بسته به فناوری سمت سرور مورد استفاده متفاوت خواهد بود.
مثال با استفاده از Node.js و Express:
const express = require('express');
const cors = require('cors');
const app = express();
// فعال کردن CORS برای همه مبداها
app.use(cors());
// به صورت جایگزین، پیکربندی CORS برای مبداهای خاص
// const corsOptions = {
// origin: 'http://www.example.com'
// };
// app.use(cors(corsOptions));
app.get('/resource', (req, res) => {
res.json({ message: 'This is a CORS-enabled resource' });
});
app.listen(3000, () => {
console.log('Server listening on port 3000');
});
میانافزار cors فرآیند تنظیم هدرهای CORS در Express را ساده میکند. میتوانید CORS را برای همه مبداها با استفاده از cors() فعال کنید یا آن را برای مبداهای خاص با cors(corsOptions) پیکربندی کنید.
مثال با استفاده از پایتون و Flask:
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
@app.route("/resource")
def hello():
return {"message": "This is a CORS-enabled resource"}
if __name__ == '__main__':
app.run(debug=True)
افزونه flask_cors راه سادهای برای فعال کردن CORS در اپلیکیشنهای Flask فراهم میکند. میتوانید با ارسال app به CORS()، CORS را برای همه مبداها فعال کنید. پیکربندی برای مبداهای خاص نیز امکانپذیر است.
مثال با استفاده از جاوا و Spring Boot:
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/resource")
.allowedOrigins("http://www.example.com")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("Content-Type", "X-Custom-Header")
.allowCredentials(true)
.maxAge(3600);
}
}
در Spring Boot، میتوانید CORS را با استفاده از WebMvcConfigurer پیکربندی کنید. این امکان کنترل دقیق بر روی مبداها، متدها، هدرها و سایر تنظیمات CORS را فراهم میکند.
تنظیم مستقیم هدرهای CORS (مثال عمومی)
اگر از هیچ فریمورکی استفاده نمیکنید، میتوانید هدرها را مستقیماً در کد سمت سرور خود تنظیم کنید (مثلاً PHP, Ruby on Rails, و غیره):
بهترین شیوهها برای CORS
برای اطمینان از ارتباط امن و کارآمد میان-مبدا، این بهترین شیوهها را دنبال کنید:
- از استفاده از
Access-Control-Allow-Origin: *در محیط پروداکشن خودداری کنید: اجازه دسترسی همه مبداها به منابع شما میتواند یک خطر امنیتی باشد. به جای آن، مبداهای مجاز را به طور دقیق مشخص کنید. - از HTTPS استفاده کنید: همیشه از HTTPS برای هر دو مبدا درخواستکننده و سرویسدهنده برای محافظت از دادهها در حین انتقال استفاده کنید.
- ورودیها را اعتبارسنجی کنید: همیشه دادههای دریافت شده از درخواستهای میان-مبدا را برای جلوگیری از حملات تزریق، اعتبارسنجی و پاکسازی کنید.
- احراز هویت و مجوزدهی مناسب را پیادهسازی کنید: اطمینان حاصل کنید که فقط کاربران مجاز میتوانند به منابع حساس دسترسی داشته باشند.
- پاسخهای پیشپرواز را کش کنید: از
Access-Control-Max-Ageبرای کش کردن پاسخهای پیشپرواز و کاهش تعداد درخواستهایOPTIONSاستفاده کنید. - استفاده از Credentials را در نظر بگیرید: اگر API شما به احراز هویت با کوکیها یا احراز هویت HTTP نیاز دارد، باید هدر
Access-Control-Allow-Credentialsرا رویtrueدر سرور و گزینهcredentialsرا روی'include'در کد جاوا اسکریپت خود تنظیم کنید (مثلاً هنگام استفاده ازfetchیاXMLHttpRequest). هنگام استفاده از این گزینه بسیار مراقب باشید، زیرا در صورت عدم مدیریت صحیح میتواند آسیبپذیریهای امنیتی ایجاد کند. همچنین، وقتی Access-Control-Allow-Credentials روی true تنظیم میشود، Access-Control-Allow-Origin نمیتواند روی "*" تنظیم شود. شما باید به صراحت مبدا(های) مجاز را مشخص کنید. - پیکربندی CORS را به طور منظم بازبینی و بهروزرسانی کنید: با تکامل اپلیکیشن خود، پیکربندی CORS خود را به طور منظم بازبینی و بهروزرسانی کنید تا اطمینان حاصل شود که امن باقی میماند و نیازهای شما را برآورده میکند.
- پیامدهای پیکربندیهای مختلف CORS را درک کنید: از پیامدهای امنیتی پیکربندیهای مختلف CORS آگاه باشید و پیکربندی مناسب برای اپلیکیشن خود را انتخاب کنید.
- پیادهسازی CORS خود را تست کنید: پیادهسازی CORS خود را به طور کامل تست کنید تا اطمینان حاصل شود که همانطور که انتظار میرود کار میکند و هیچ آسیبپذیری امنیتی ایجاد نمیکند. از ابزارهای توسعهدهنده مرورگر برای بازرسی درخواستها و پاسخهای شبکه و از ابزارهای تست خودکار برای تأیید رفتار CORS استفاده کنید.
مثال: استفاده از Fetch API با CORS
در اینجا مثالی از نحوه استفاده از fetch API برای ارسال یک درخواست میان-مبدا آورده شده است:
fetch('https://api.example.com/data', {
method: 'GET',
mode: 'cors', // به مرورگر میگوید که این یک درخواست CORS است
headers: {
'Content-Type': 'application/json',
'X-Custom-Header': 'value'
}
})
.then(response => {
if (!response.ok) {
throw new Error('پاسخ شبکه موفقیتآمیز نبود');
}
return response.json();
})
.then(data => {
console.log(data);
})
.catch(error => {
console.error('در عملیات fetch مشکلی پیش آمد:', error);
});
گزینه mode: 'cors' به مرورگر میگوید که این یک درخواست CORS است. اگر سرور به مبدا اجازه ندهد، مرورگر دسترسی به پاسخ را مسدود کرده و یک خطا پرتاب خواهد شد.
اگر از credentials (مانند کوکیها) استفاده میکنید، باید گزینه credentials را روی 'include' تنظیم کنید:
fetch('https://api.example.com/data', {
method: 'GET',
mode: 'cors',
credentials: 'include', // کوکیها را در درخواست لحاظ کن
headers: {
'Content-Type': 'application/json'
}
})
.then(response => {
// ...
});
CORS و JSONP
JSON with Padding (JSONP) یک تکنیک قدیمیتر برای دور زدن سیاست همان-مبدا است. این تکنیک با ایجاد پویا یک تگ <script> کار میکند که دادهها را از یک دامنه دیگر بارگیری میکند. در حالی که JSONP میتواند در شرایط خاص مفید باشد، محدودیتهای امنیتی قابل توجهی دارد و در صورت امکان باید از آن اجتناب شود. CORS راه حل ترجیحی برای ارتباطات میان-مبدا است زیرا مکانیزم امنتر و انعطافپذیرتری را فراهم میکند.
تفاوتهای کلیدی بین CORS و JSONP:
- امنیت: CORS امنتر از JSONP است زیرا به سرور اجازه میدهد کنترل کند کدام مبداها مجاز به دسترسی به منابع آن هستند. JSONP هیچ کنترل مبدایی را فراهم نمیکند.
- متدهای HTTP: CORS از تمام متدهای HTTP (مانند
GET،POST،PUT،DELETE) پشتیبانی میکند، در حالی که JSONP فقط از درخواستهایGETپشتیبانی میکند. - مدیریت خطا: CORS مدیریت خطای بهتری نسبت به JSONP ارائه میدهد. هنگامی که یک درخواست CORS با شکست مواجه میشود، مرورگر پیامهای خطای دقیقی ارائه میدهد. مدیریت خطای JSONP به تشخیص اینکه آیا اسکریپت با موفقیت بارگیری شده است یا خیر محدود میشود.
عیبیابی مشکلات CORS
عیبیابی مشکلات CORS میتواند خستهکننده باشد. در اینجا چند نکته رایج برای عیبیابی آورده شده است:
- کنسول مرورگر را بررسی کنید: کنسول مرورگر معمولاً پیامهای خطای دقیقی در مورد مشکلات CORS ارائه میدهد.
- درخواستهای شبکه را بازرسی کنید: از ابزارهای توسعهدهنده مرورگر برای بازرسی هدرهای HTTP هم در درخواست و هم در پاسخ استفاده کنید. تأیید کنید که هدرهای
OriginوAccess-Control-Allow-Originبه درستی تنظیم شدهاند. - پیکربندی سمت سرور را تأیید کنید: پیکربندی CORS سمت سرور خود را دوباره بررسی کنید تا اطمینان حاصل شود که به مبداها، متدها و هدرهای صحیح اجازه میدهد.
- حافظه پنهان مرورگر را پاک کنید: گاهی اوقات، پاسخهای پیشپرواز کش شده میتوانند باعث مشکلات CORS شوند. سعی کنید حافظه پنهان مرورگر خود را پاک کنید یا از یک پنجره مرور خصوصی استفاده کنید.
- از یک پروکسی CORS استفاده کنید: در برخی موارد، ممکن است نیاز به استفاده از یک پروکسی CORS برای دور زدن محدودیتهای CORS داشته باشید. با این حال، آگاه باشید که استفاده از پروکسی CORS میتواند خطرات امنیتی ایجاد کند.
- پیکربندیهای نادرست را بررسی کنید: به دنبال پیکربندیهای نادرست رایج مانند نبود هدر
Access-Control-Allow-Origin، مقادیر نادرستAccess-Control-Allow-MethodsیاAccess-Control-Allow-Headers، یا هدرOriginنادرست در درخواست باشید.
نتیجهگیری
اشتراکگذاری منابع میان-مبدا (CORS) یک مکانیزم ضروری برای فعال کردن ارتباطات امن میان-مبدا در اپلیکیشنهای جاوا اسکریپت است. با درک سیاست همان-مبدا، گردش کار CORS و هدرهای مختلف HTTP درگیر، توسعهدهندگان میتوانند CORS را به طور مؤثر پیادهسازی کنند تا از اپلیکیشنهای خود در برابر آسیبپذیریهای امنیتی محافظت کنند و در عین حال به درخواستهای قانونی میان-مبدا اجازه دهند. پیروی از بهترین شیوهها برای پیکربندی CORS و بازبینی منظم پیادهسازی شما برای حفظ یک اپلیکیشن وب امن و قوی حیاتی است.
این راهنمای جامع، پایهای محکم برای درک و پیادهسازی CORS فراهم میکند. به یاد داشته باشید که برای اطمینان از پیادهسازی صحیح و امن CORS، به مستندات رسمی و منابع مربوط به فناوری سمت سرور خاص خود مراجعه کنید.