استكشاف متعمق لمشاركة الموارد عبر المصادر (CORS) والطلبات الأولية. تعلم كيفية التعامل مع مشكلات CORS وتأمين تطبيقات الويب الخاصة بك لجمهور عالمي.
تبسيط CORS: نظرة متعمقة في معالجة طلبات JavaScript الأولية
في عالم تطوير الويب المتوسع باستمرار، يعتبر الأمان أمرًا بالغ الأهمية. تعتبر مشاركة الموارد عبر المصادر (CORS) آلية أمان حاسمة يتم تنفيذها بواسطة متصفحات الويب لتقييد صفحات الويب من تقديم طلبات إلى مجال مختلف عن المجال الذي عرض صفحة الويب. هذه ميزة أمان أساسية مصممة لمنع مواقع الويب الضارة من الوصول إلى البيانات الحساسة. سيتعمق هذا الدليل الشامل في تعقيدات CORS، مع التركيز بشكل خاص على معالجة الطلبات الأولية. سنستكشف "لماذا" و"ماذا" و"كيف" من CORS، ونقدم أمثلة وحلول عملية للمشكلات الشائعة التي يواجهها المطورون في جميع أنحاء العالم.
فهم سياسة الأصل نفسه
يكمن في قلب CORS سياسة الأصل نفسه (SOP). هذه السياسة هي آلية أمان على مستوى المتصفح تقيد البرامج النصية التي تعمل على أصل واحد من الوصول إلى موارد من أصل مختلف. يتم تعريف الأصل بواسطة البروتوكول (مثل HTTP أو HTTPS) والمجال (مثل example.com) والمنفذ (مثل 80 أو 443). عنوانا URL لهما نفس الأصل إذا تطابقت هذه المكونات الثلاثة تمامًا.
على سبيل المثال:
https://www.example.com/app1/index.htmlوhttps://www.example.com/app2/index.htmlلهما نفس الأصل (نفس البروتوكول والمجال والمنفذ).https://www.example.com/index.htmlوhttp://www.example.com/index.htmlلهما أصلان مختلفان (بروتوكولات مختلفة).https://www.example.com/index.htmlوhttps://api.example.com/index.htmlلهما أصلان مختلفان (تعتبر النطاقات الفرعية المختلفة مجالات مختلفة).https://www.example.com:8080/index.htmlوhttps://www.example.com/index.htmlلهما أصلان مختلفان (منافذ مختلفة).
تم تصميم SOP لمنع البرامج النصية الضارة على أحد مواقع الويب من الوصول إلى البيانات الحساسة، مثل ملفات تعريف الارتباط أو معلومات مصادقة المستخدم، على موقع ويب آخر. على الرغم من أهمية SOP للأمان، إلا أنها يمكن أن تكون مقيدة أيضًا، خاصةً عندما تكون هناك حاجة إلى طلبات مشروعة عبر المصادر.
ما هي مشاركة الموارد عبر المصادر (CORS)؟
CORS هي آلية تسمح للخوادم بتحديد الأصول (المجالات أو المخططات أو المنافذ) المسموح لها بالوصول إلى مواردها. إنه يخفف بشكل أساسي من SOP، مما يسمح بالوصول المتحكم فيه عبر المصادر. يتم تنفيذ CORS باستخدام رؤوس HTTP التي يتم تبادلها بين العميل (عادةً متصفح الويب) والخادم.
عندما يقدم متصفح طلبًا عبر المصادر (أي طلبًا إلى أصل مختلف عن الصفحة الحالية)، فإنه يتحقق أولاً مما إذا كان الخادم يسمح بالطلب. يتم ذلك عن طريق فحص رأس Access-Control-Allow-Origin في استجابة الخادم. إذا كان أصل الطلب مدرجًا في هذا الرأس (أو إذا تم تعيين الرأس على *، مما يسمح بجميع الأصول)، يسمح المتصفح بالمتابعة في الطلب. بخلاف ذلك، يحظر المتصفح الطلب، مما يمنع كود JavaScript من الوصول إلى بيانات الاستجابة.
دور الطلبات الأولية
بالنسبة لأنواع معينة من الطلبات عبر المصادر، يبدأ المتصفح طلبًا أوليًا. هذا هو طلب OPTIONS يتم إرساله إلى الخادم قبل الطلب الفعلي. الغرض من الطلب الأولي هو تحديد ما إذا كان الخادم على استعداد لقبول الطلب الفعلي. يستجيب الخادم للطلب الأولي بمعلومات حول الطرق المسموح بها والرؤوس والقيود الأخرى.
يتم تشغيل الطلبات الأولية عندما يستوفي الطلب عبر المصادر أي من الشروط التالية:
- طريقة الطلب ليست
GETأوHEADأوPOST. - يتضمن الطلب رؤوسًا مخصصة (أي رؤوسًا أخرى غير تلك التي يضيفها المتصفح تلقائيًا).
- تم تعيين رأس
Content-Typeعلى أي شيء بخلافapplication/x-www-form-urlencodedأوmultipart/form-dataأوtext/plain. - يستخدم الطلب كائنات
ReadableStreamفي النص الأساسي.
على سبيل المثال، سيؤدي طلب PUT مع Content-Type من application/json إلى تشغيل طلب أولي لأنه يستخدم طريقة مختلفة عن الطرق المسموح بها ونوع محتوى قد يكون غير مسموح به.
لماذا الطلبات الأولية؟
تعتبر الطلبات الأولية ضرورية للأمان لأنها تتيح للخادم فرصة لرفض الطلبات المحتملة الضارة عبر المصادر قبل تنفيذها. بدون طلبات أولية، يمكن لموقع ويب ضار إرسال طلبات عشوائية إلى خادم دون موافقة الخادم الصريحة. يسمح الطلب الأولي للخادم بالتحقق من صحة أن الطلب مقبول ويمنع العمليات التي قد تكون ضارة.
التعامل مع الطلبات الأولية على جانب الخادم
تعد معالجة الطلبات الأولية بشكل صحيح أمرًا بالغ الأهمية لضمان عمل تطبيق الويب الخاص بك بشكل صحيح وآمن. يجب أن يستجيب الخادم لطلب OPTIONS برؤوس CORS المناسبة للإشارة إلى ما إذا كان الطلب الفعلي مسموحًا به أم لا.
فيما يلي تفصيل لرؤوس CORS الرئيسية المستخدمة في الاستجابات الأولية:
Access-Control-Allow-Origin: يحدد هذا الرأس الأصل (الأصول) المسموح لها بالوصول إلى المورد. يمكن تعيينه على أصل معين (مثلhttps://www.example.com) أو على*للسماح لجميع الأصول. ومع ذلك، لا يُنصح عمومًا باستخدام*لأسباب أمنية، خاصة إذا كان الخادم يتعامل مع البيانات الحساسة.Access-Control-Allow-Methods: يحدد هذا الرأس طرق HTTP المسموح بها للطلب عبر المصادر (مثلGETوPOSTوPUTوDELETE).Access-Control-Allow-Headers: يحدد هذا الرأس قائمة رؤوس HTTP غير القياسية المسموح بها في الطلب الفعلي. هذا ضروري إذا كان العميل يرسل رؤوسًا مخصصة، مثلX-Custom-HeaderأوAuthorization.Access-Control-Allow-Credentials: يشير هذا الرأس إلى ما إذا كان الطلب الفعلي يمكن أن يتضمن بيانات اعتماد، مثل ملفات تعريف الارتباط أو رؤوس التخويل. يجب تعيينه علىtrueإذا كان كود جانب العميل يرسل بيانات اعتماد ويجب على الخادم قبولها. ملاحظة: عند تعيين هذا الرأس على `true`، *لا يمكن* تعيين `Access-Control-Allow-Origin` على `*`. يجب عليك تحديد الأصل.Access-Control-Max-Age: يحدد هذا الرأس الحد الأقصى للوقت (بالثواني) الذي يمكن للمتصفح تخزين الاستجابة الأولية مؤقتًا. يمكن أن يساعد ذلك في تحسين الأداء عن طريق تقليل عدد الطلبات الأولية التي يتم إرسالها.
مثال: معالجة الطلبات الأولية في Node.js مع Express
فيما يلي مثال لكيفية معالجة الطلبات الأولية في تطبيق Node.js باستخدام إطار عمل Express:
const express = require('express');
const cors = require('cors');
const app = express();
// Enable CORS for all origins (for development purposes only!)
// In production, specify allowed origins for better security.
app.use(cors()); //or app.use(cors({origin: 'https://www.example.com'}));
// Route for handling OPTIONS requests (preflight)
app.options('/data', cors()); // Enable CORS for a single route. Or specify origin: cors({origin: 'https://www.example.com'})
// Route for handling GET requests
app.get('/data', (req, res) => {
res.json({ message: 'This is cross-origin data!' });
});
// Route to handle a preflight and a post request
app.options('/resource', cors()); // enable pre-flight request for DELETE request
app.delete('/resource', cors(), (req, res, next) => {
res.send('delete resource')
})
const port = 3000;
app.listen(port, () => {
console.log(`Server listening on port ${port}`);
});
في هذا المثال، نستخدم وسيطة cors للتعامل مع طلبات CORS. لمزيد من التحكم التفصيلي، يمكن تمكين CORS على أساس كل مسار. ملاحظة: في الإنتاج، يوصى بشدة بتحديد الأصول المسموح بها باستخدام خيار origin بدلاً من السماح بجميع الأصول. يمكن أن يؤدي السماح لجميع الأصول باستخدام * إلى تعريض تطبيقك لثغرات أمنية.
مثال: معالجة الطلبات الأولية في Python مع Flask
فيما يلي مثال لكيفية معالجة الطلبات الأولية في تطبيق Python باستخدام إطار عمل Flask وامتداد flask_cors:
from flask import Flask, jsonify
from flask_cors import CORS, cross_origin
app = Flask(__name__)
CORS(app) # Enable CORS for all routes
@app.route('/data')
@cross_origin()
def get_data():
data = {"message": "This is cross-origin data!"}
return jsonify(data)
if __name__ == '__main__':
app.run(debug=True)
هذا هو أبسط استخدام. كما كان من قبل، يمكن تقييد الأصول. راجع وثائق flask-cors للحصول على التفاصيل.
مثال: معالجة الطلبات الأولية في Java مع Spring Boot
فيما يلي مثال لكيفية معالجة الطلبات الأولية في تطبيق Java باستخدام Spring Boot:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@SpringBootApplication
public class CorsApplication {
public static void main(String[] args) {
SpringApplication.run(CorsApplication.class, args);
}
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/data").allowedOrigins("http://localhost:8080");
}
};
}
}
ووحدة التحكم المقابلة:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DataController {
@GetMapping("/data")
public String getData() {
return "This is cross-origin data!";
}
}
مشكلات CORS الشائعة وحلولها
على الرغم من أهميته، غالبًا ما يكون CORS مصدر إحباط للمطورين. فيما يلي بعض مشكلات CORS الشائعة وحلولها:
-
خطأ: "لا يوجد رأس 'Access-Control-Allow-Origin' في المورد المطلوب."
يشير هذا الخطأ إلى أن الخادم لا يُرجع رأس
Access-Control-Allow-Originفي استجابته. لإصلاح ذلك، تأكد من تكوين الخادم لتضمين الرأس وأنه تم تعيينه على الأصل الصحيح أو على*(إذا كان ذلك مناسبًا).الحل: قم بتكوين الخادم لتضمين رأس `Access-Control-Allow-Origin` في استجابته، وتعيينه على أصل موقع الويب الذي يطلب أو على `*` للسماح لجميع الأصول (استخدمه بحذر).
-
خطأ: "الاستجابة للطلب الأولي لا تجتاز فحص التحكم في الوصول: حقل رأس الطلب X-Custom-Header غير مسموح به بواسطة Access-Control-Allow-Headers في الاستجابة الأولية."
يشير هذا الخطأ إلى أن الخادم لا يسمح بالرأس المخصص (
X-Custom-Headerفي هذا المثال) في الطلب عبر المصادر. لإصلاح ذلك، تأكد من أن الخادم يتضمن الرأس في رأسAccess-Control-Allow-Headersفي الاستجابة الأولية.الحل: أضف الرأس المخصص (مثل `X-Custom-Header`) إلى رأس `Access-Control-Allow-Headers` في الاستجابة الأولية للخادم.
-
خطأ: "علامة بيانات الاعتماد هي 'true'، ولكن رأس 'Access-Control-Allow-Origin' هو '*'."
عندما يتم تعيين رأس
Access-Control-Allow-Credentialsعلىtrue، يجب تعيين رأسAccess-Control-Allow-Originعلى أصل معين، وليس*. وذلك لأن السماح ببيانات الاعتماد من جميع الأصول سيكون خطرًا أمنيًا.الحل: عند استخدام بيانات الاعتماد، قم بتعيين `Access-Control-Allow-Origin` على أصل معين بدلاً من `*`.
-
لم يتم إرسال الطلب الأولي.
تحقق جيدًا من أن كود Javascript الخاص بك يتضمن الخاصية `credentials: 'include'`. تحقق أيضًا من أن الخادم الخاص بك يسمح بـ `Access-Control-Allow-Credentials: true`.
-
تكوينات متضاربة بين الخادم والعميل.
تحقق بعناية من تكوين CORS الخاص بك على جانب الخادم جنبًا إلى جنب مع إعدادات جانب العميل. ستؤدي حالات عدم التطابق (مثل الخادم الذي يسمح فقط بطلبات GET ولكن العميل يرسل POST) إلى حدوث أخطاء CORS.
أفضل ممارسات CORS والأمان
في حين أن CORS يسمح بالوصول المتحكم فيه عبر المصادر، فمن الضروري اتباع أفضل الممارسات الأمنية لمنع الثغرات الأمنية:
- تجنب استخدام
*في رأسAccess-Control-Allow-Originفي الإنتاج. يسمح ذلك لجميع الأصول بالوصول إلى مواردك، مما قد يشكل خطرًا أمنيًا. بدلاً من ذلك، حدد الأصول الدقيقة المسموح بها. - ضع في اعتبارك بعناية الطرق والرؤوس التي يجب السماح بها. اسمح فقط بالطرق والرؤوس الضرورية تمامًا لكي يعمل تطبيقك بشكل صحيح.
- قم بتنفيذ آليات المصادقة والتخويل المناسبة. CORS ليس بديلاً عن المصادقة والتخويل. تأكد من أن واجهة برمجة التطبيقات الخاصة بك محمية بإجراءات أمنية مناسبة.
- تحقق من صحة جميع مدخلات المستخدم وتطهيرها. يساعد هذا في منع هجمات البرمجة النصية عبر المواقع (XSS) وغيرها من الثغرات الأمنية.
- حافظ على تحديث تكوين CORS الخاص بك على جانب الخادم. قم بمراجعة وتحديث تكوين CORS الخاص بك بانتظام للتأكد من توافقه مع متطلبات الأمان لتطبيقك.
CORS في بيئات التطوير المختلفة
يمكن أن تظهر مشكلات CORS بشكل مختلف عبر بيئات وتقنيات تطوير مختلفة. فيما يلي نظرة على كيفية التعامل مع CORS في عدد قليل من السيناريوهات الشائعة:
بيئات التطوير المحلية
أثناء التطوير المحلي، يمكن أن تكون مشكلات CORS مزعجة بشكل خاص. غالبًا ما تحظر المتصفحات الطلبات من خادم التطوير المحلي (مثل localhost:3000) إلى واجهة برمجة تطبيقات بعيدة. يمكن للعديد من التقنيات تخفيف هذا الألم:
- ملحقات المتصفح: يمكن لملحقات مثل "السماح بـ CORS: Access-Control-Allow-Origin" تعطيل قيود CORS مؤقتًا لأغراض الاختبار. ومع ذلك، *لا* تستخدم هذه في الإنتاج أبدًا.
- خوادم الوكيل: قم بتكوين خادم وكيل يقوم بإعادة توجيه الطلبات من خادم التطوير المحلي الخاص بك إلى واجهة برمجة التطبيقات البعيدة. هذا يجعل الطلبات بشكل فعال "من نفس الأصل" من منظور المتصفح. أدوات مثل
http-proxy-middleware(لـ Node.js) مفيدة لهذا الغرض. - تكوين CORS للخادم: حتى أثناء التطوير، من الأفضل تكوين خادم API الخاص بك للسماح صراحةً بالطلبات من أصل التطوير المحلي الخاص بك (مثل
http://localhost:3000). هذا يحاكي تكوين CORS في العالم الحقيقي ويساعدك على اكتشاف المشكلات مبكرًا.
بيئات بدون خادم (مثل AWS Lambda، Google Cloud Functions)
غالبًا ما تتطلب الوظائف بدون خادم تكوينًا دقيقًا لـ CORS. توفر العديد من الأنظمة الأساسية بدون خادم دعمًا مدمجًا لـ CORS، ولكن من الضروري تكوينه بشكل صحيح:
- إعدادات خاصة بالنظام الأساسي: استخدم خيارات تكوين CORS المضمنة في النظام الأساسي. يسمح لك AWS Lambda، على سبيل المثال، بتحديد الأصول والطرق والرؤوس المسموح بها مباشرةً في إعدادات API Gateway.
- البرامج الوسيطة/المكتبات: لمزيد من المرونة، يمكنك استخدام البرامج الوسيطة أو المكتبات للتعامل مع CORS داخل كود الوظيفة بدون خادم. هذا مشابه للطرق المستخدمة في بيئات الخادم التقليدية (مثل استخدام حزمة `cors` في وظائف Node.js Lambda).
- ضع في اعتبارك طريقة `OPTIONS`: تأكد من أن وظيفتك بدون خادم تتعامل مع طلبات
OPTIONSبشكل صحيح. غالبًا ما يتضمن ذلك إنشاء مسار منفصل يُرجع رؤوس CORS المناسبة.
تطوير تطبيقات الأجهزة المحمولة (مثل React Native، Flutter)
تعتبر CORS أقل أهمية مباشرة لتطبيقات الأجهزة المحمولة الأصلية (Android و iOS)، لأنها لا تفرض عادةً سياسة الأصل نفسه بنفس الطريقة التي تفرضها متصفحات الويب. ومع ذلك، يمكن أن تظل CORS ذات صلة إذا كان تطبيقك المحمول يستخدم عرض ويب لعرض محتوى الويب أو إذا كنت تستخدم أطر عمل مثل React Native أو Flutter التي تستفيد من JavaScript:
- عروض الويب: إذا كان تطبيقك المحمول يستخدم عرض ويب لعرض محتوى الويب، فتنطبق نفس قواعد CORS كما في متصفح الويب. قم بتكوين الخادم الخاص بك للسماح بالطلبات من أصل محتوى الويب.
- React Native/Flutter: تستخدم أطر العمل هذه JavaScript لتقديم طلبات API. على الرغم من أن البيئة الأصلية قد لا تفرض CORS مباشرةً، إلا أن عملاء HTTP الأساسيين (مثل
fetch) قد يظهرون سلوكًا مشابهًا لـ CORS في مواقف معينة. - عملاء HTTP الأصليون: عند تقديم طلبات API مباشرةً من الكود الأصلي (مثل استخدام OkHttp على Android أو URLSession على iOS)، لا يعتبر CORS عاملاً بشكل عام. ومع ذلك، لا تزال بحاجة إلى مراعاة أفضل الممارسات الأمنية مثل المصادقة والتخويل المناسبين.
اعتبارات عالمية لتكوين CORS
عند تكوين CORS لتطبيق يمكن الوصول إليه عالميًا، من الضروري مراعاة عوامل مثل:
- سيادة البيانات: تنص اللوائح في بعض المناطق على أن البيانات يجب أن تقيم داخل المنطقة. قد تشارك CORS عند الوصول إلى الموارد عبر الحدود، مما قد يتعارض مع قوانين الإقامة في البيانات.
- سياسات الأمان الإقليمية: قد يكون لدى البلدان المختلفة لوائح وإرشادات مختلفة للأمن السيبراني تؤثر على كيفية تنفيذ CORS وتأمينها.
- شبكات توصيل المحتوى (CDNs): تأكد من تكوين CDN الخاص بك بشكل صحيح لتمرير رؤوس CORS الضرورية. يمكن لشبكات CDN التي تم تكوينها بشكل غير صحيح إزالة رؤوس CORS، مما يؤدي إلى أخطاء غير متوقعة.
- موازنات التحميل والوكلاء: تحقق من أن أي موازنات تحميل أو وكلاء عكسيين في البنية التحتية الخاصة بك يتعاملون بشكل صحيح مع الطلبات الأولية ويمررون رؤوس CORS.
- دعم متعدد اللغات: ضع في اعتبارك كيف تتفاعل CORS مع استراتيجيات تدويل (i18n) وتوطين (l10n) تطبيقك. تأكد من أن سياسات CORS متسقة عبر إصدارات اللغات المختلفة لتطبيقك.
اختبار وتصحيح CORS
يعد اختبار وتصحيح CORS بشكل فعال أمرًا حيويًا. فيما يلي بعض التقنيات:
- أدوات مطوري المتصفح: وحدة تحكم مطوري المتصفح هي محطتك الأولى. ستعرض علامة التبويب "الشبكة" الطلبات الأولية والاستجابات، مما يكشف عما إذا كانت رؤوس CORS موجودة وتم تكوينها بشكل صحيح.
- أداة سطر الأوامر `curl`: استخدم `curl -v -X OPTIONS
` لإرسال الطلبات الأولية يدويًا وفحص رؤوس استجابة الخادم. - مدققو CORS عبر الإنترنت: يمكن للعديد من الأدوات عبر الإنترنت المساعدة في التحقق من صحة تكوين CORS الخاص بك. ما عليك سوى البحث عن "مدقق CORS".
- اختبارات الوحدة والتكامل: اكتب اختبارات آلية للتحقق من أن تكوين CORS الخاص بك يعمل كما هو متوقع. يجب أن تغطي هذه الاختبارات كلاً من الطلبات الناجحة عبر المصادر والسيناريوهات التي يجب أن تحظر فيها CORS الوصول.
- تسجيل ومراقبة: قم بتنفيذ التسجيل لتتبع الأحداث المتعلقة بـ CORS، مثل الطلبات الأولية والطلبات المحظورة. راقب سجلاتك بحثًا عن نشاط مشبوه أو أخطاء في التكوين.
خاتمة
تعتبر مشاركة الموارد عبر المصادر (CORS) آلية أمان حيوية تتيح الوصول المتحكم فيه عبر المصادر إلى موارد الويب. يعد فهم كيفية عمل CORS، وخاصة الطلبات الأولية، أمرًا بالغ الأهمية لبناء تطبيقات ويب آمنة وموثوقة. باتباع أفضل الممارسات الموضحة في هذا الدليل، يمكنك التعامل بفعالية مع مشكلات CORS وحماية تطبيقك من الثغرات الأمنية المحتملة. تذكر دائمًا إعطاء الأولوية للأمان والنظر بعناية في الآثار المترتبة على تكوين CORS الخاص بك.
مع تطور تطوير الويب، سيظل CORS جانبًا مهمًا من جوانب أمان الويب. يعد البقاء على اطلاع بأحدث أفضل ممارسات وتقنيات CORS أمرًا ضروريًا لبناء تطبيقات ويب آمنة ويمكن الوصول إليها عالميًا.