تعرف على الفرق بين طلب HTTP البسيط وطلب XHR.
تطبيق ويب الأصل - https://github.com
عند فتح موقع ويب عن طريق كتابة Origin Web App، يقوم متصفحك بإجراء طلبات HTTP بسيطة. CORS غير قابل للتطبيق هنا. نظرًا لأننا نطلب طوعًا تقديم المحتوى من Origin Web App. متصفحك سوف/ينبغي أن يخدمه دون أي مشكلة. ولكن، عندما يتعلق الأمر بطلبات XHR، يمكن لـ JavaScript الذي يتم تحميله بواسطة Origin Web App في متصفحك تقديم طلب إلى أي خادم (https://netflix.com) يطلب موردًا. الآن، Origin Web App مملوك لشركة Github ولكن https://netflix.com مملوك لشركة Netflix، ويمكن لهذا الخادم أن يخدم أي شيء بشكل فعال. لا يمكن لـ Github التحكم في خادم مملوك لشركة Netflix. وهذا له الكثير من الآثار الأمنية، مثل سرقة محتوى من موقع ويب واحد (يمكن أن يكون موقعًا ماليًا) إلى أي خادم بعيد.
لحسن الحظ، تعالج CORS هذه المشكلة بشكل جيد للغاية من خلال مجموعة معينة من القواعد.
إنه معيار تم تحديده بواسطة W3C لتمكين الطلبات المشتركة بين العميل (المتصفح) والخادم لمشاركة الموارد في نفس الوقت مع الحفاظ على الأمان. سيلتزم أي متصفح بهذه المعايير لمنع تحميل الموارد من أي خوادم تابعة لجهات خارجية.
يشير الرأس إلى المكان الذي نشأ منه الطلب. يتم إرساله مع طلبات CORS، وكذلك مع طلبات POST.
بناء الجملة:
Origin: <scheme> "://" <hostname> [":" <port> ]
أمثلة:
Origin: https://netflix.com
Origin: http://netflix.com:443
Origin: http://localhost:1443
يستخدم المتصفح هذا الرأس عند إصدار طلبات ما قبل الرحلة للإشارة إلى طريقة الطلب التي سيتم استخدامها عند تقديم الطلب الفعلي.
بناء الجملة:
Access-Control-Request-Method: <method>
<method>
يمكن أن يكون إما GET
أو POST
أو DELETE
.
مثال:
Access-Control-Request-Method: POST
يتم استخدام هذا الرأس مرة أخرى في طلبات ما قبل الرحلة بواسطة المتصفح للإشارة إلى رؤوس الطلبات التي سيتم استخدامها عند تقديم الطلب الفعلي. لاستخدام رؤوس متعددة، يجب فصلها بفاصلة.
بناء الجملة:
Access-Control-Request-Headers: <header-name>
Access-Control-Request-Headers: <header-name>, <header-name>
مثال:
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
هام: يجب أن تكون كل الرؤوس مدرجة في القائمة الآمنة لـ CORS أو رأسًا مخصصًا مثل X-Custom-Header
.
Accept
Accept-Language
Content-Language
Content-Type
القيم المسموح بها هي application/x-www-form-urlencoded
و multipart/form-data
و text/plain
.
يتم إرجاع الرؤوس التالية في طلبات ما قبل الرحلة.
يشير هذا الرأس إلى أنه إذا تم السماح بالأصل المطلوب. سيختار متصفحك نجاح/فشل الطلب عن طريق مطابقة الأصل المطلوب مع هذا.
بناء الجملة:
Access-Control-Allow-Origin: *
بالنسبة للطلبات التي لا تحتوي على بيانات اعتماد، يمكن تحديد القيمة *
كحرف بدل. هذا يخبر متصفحك بالسماح بالطلبات من أي أصل.
Access-Control-Allow-Origin: <origin>
عندما تتلقى أصلًا واحدًا فقط في رأس الاستجابة، فهذا يعني أن خادمك/تطبيق الويب الخاص بك استنادًا إلى Origin
المطلوب فإنه يستجيب بنفس Origin
إذا كان مسموحًا به. يجب أن يستجيب خادمك أيضًا بـ Vary للإشارة إلى أنه يختلف بناءً على رأس الطلب.
مثال:
Access-Control-Allow-Origin: https://github.com
سيقدم متصفحك طلبًا فعليًا إذا تطابقت إحدى القيم الموجودة في هذا الرأس. عندما يتم إرجاع حرف البدل *
، فهذا يعني أن أي طريقة مسموح بها.
بناء الجملة:
Access-Control-Allow-Methods: <method>, <method>, ...
Access-Control-Allow-Methods: *
مثال:
Access-Control-Allow-Methods: GET, POST
سيقوم متصفحك بتقديم الطلب الفعلي إذا تم السماح بجميع الرؤوس المطلوبة.
بناء الجملة:
Access-Control-Allow-Headers: <header-name>, <header-name>
Access-Control-Allow-Headers: *
مثال:
Access-Control-Allow-Headers: Accept, Content-Type
Wildcard *
يخبر المتصفح بأن أي رأس مسموح به.
يوضح المدة التي يمكن خلالها تخزين نتائج ما قبل الرحلة مؤقتًا.
Access-Control-Max-Age: <delta-seconds>
الحد الأقصى لا. من الثواني يمكن تخزين النتائج مؤقتًا.
كل متصفح له الحد الأقصى،
يختلف الرأس بشكل عام لغرض التخزين المؤقت لتحديد ما إذا كان يمكن استخدام الاستجابة المخزنة مؤقتًا أو يجب إعادة تخزينها مؤقتًا بناءً على قيمة الرأس.
في CORS، لنفترض أن الخادم يسمح بأصول متعددة بناءً على Origin
المطلوب، وسيُرجع عنوان URL معينًا في Access-Control-Allow-Origin
.
بناء الجملة:
Vary: <header-name>
مثال:
لنفترض أن github.com يسمح لكل من https://github.com وكذلك https://netflix.com بطلب الموارد. النظر في السيناريوهات التالية،
السيناريو 1:
خيارات حليقة -X https://github.com/api/v1/gists/1
Origin: https://github.com
Access-Control-Request-Method: GET
Access-Control-Request-Headers: Content-Type
Access-Control-Allow-Origin: https://github.com
Vary: Origin
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type
Access-Control-Max-Age: 600
الآن في هذا السيناريو، سيقوم المتصفح بتخزين نتائج طلب ما قبل الرحلة لمدة 10 دقائق.
السيناريو 2:
خيارات حليقة -X https://github.com/api/v1/gists/1
Origin: https://netflix.com
Access-Control-Request-Method: GET
Access-Control-Request-Headers: Content-Type
Access-Control-Allow-Origin: https://netflix.com
Vary: Origin
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type
Access-Control-Max-Age: 300
يمكنك الآن ملاحظة أن Access-Control-Allow-Origin
يحتوي على https://netflix.com، وهذا مثال على كيفية اختلاف الاستجابة بناءً على Origin
المحدد. وكذلك الحد الأقصى لعمر هذه الاستجابة التي يتم تخزينها مؤقتًا لمدة 5 دقائق فقط على عكس السيناريو الأول.
لا تؤدي بعض الطلبات إلى تشغيل طلب ما قبل الرحلة لـ CORS. وهذه نسميها طلبات بسيطة . يجب أن تستوفي الشروط التالية:
إحدى الطرق المسموح بها:
بخلاف الرؤوس التي يتم تعيينها تلقائيًا بواسطة وكيل المستخدم (على سبيل المثال، Connection
أو User-Agent
)، فإن الرؤوس الوحيدة المسموح لها بتعيينها يدويًا هي رؤوس طلبات CORS المدرجة في القائمة الآمنة وما يلي:
DPR
Downlink
Save-Data
Viewport-Width
Width
لم يتم تسجيل أي مستمعين للأحداث على أي كائن XMLHttpRequestUpload
المستخدم في الطلب.
لم يتم استخدام كائن ReadableStream
في الطلب.
مثال:
طلب:
GET /api/v1/public-data/ HTTP/1.1
Host: bar.com
User-Agent: curl/7.69.1
Accept: */*
Origin: https://foo.com
رؤوس الاستجابة:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml
عندما يقوم الطلب بإرجاع *
في رأس Access-Control-Allow-Origin
. وهذا يعني أنه يمكن تقديم طلب من أي Host
. في هذه الحالة، لن يقوم متصفحك بتقديم طلب ما قبل الرحلة.
سيقوم متصفحك بتقديم طلب ما قبل الرحلة لتحديد ما إذا كان الطلب الفعلي آمنًا للإرسال.
مثال:
const xhr = new XMLHttpRequest ( ) ;
xhr . open ( 'POST' , 'https://bar.com/api/v1/post-here/' ) ;
xhr . setRequestHeader ( 'X-PINGOTHER' , 'pingpong' ) ;
xhr . setRequestHeader ( 'Content-Type' , 'application/json;charset=UTF-8' ) ;
xhr . onreadystatechange = handler ;
xhr . send ( JSON . stringify ( { "email" : "[email protected]" } ) ) ;
طلب ما قبل الرحلة:
OPTIONS /api/v1/post-here/
Host: bar.com
User-Agent: curl/7.69.1
Accept: */*
Origin: https://foo.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
HTTP/1.1 200 OK
Connection: Keep-Alive
Content-Type: application/json
Access-Control-Allow-Origin: https://foo.com
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type, Accept
الطلب الفعلي:
POST -d '{foo: bar}' /api/v1/post-here/
Host: bar.com
User-Agent: curl/7.69.1
Accept: */*
Origin: https://foo.com
X-PINGOTHER: pingpong
Content-Type: application/json;charset=UTF-8
HTTP/1.1 200 OK
Connection: Keep-Alive
Content-Type: application/json
Access-Control-Allow-Origin: https://foo.com
عند تعيين رأس غير قياسي مثل X-PINGOTHER
، فلن يعرف متصفحك ما إذا كان من الآمن تقديم الطلب أم لا. للتأكد من أمانه، يقدم متصفحك طلب OPTIONS باستخدام Access-Control-Request-Headers
الذي يحتوي على X-PINGOTHER
و Content-Type
. عند التحقق من صحة رؤوس الاستجابة لطلب ما قبل الرحلة، يقوم متصفحك بتقديم الطلب الفعلي.
بشكل عام، عند تقديم طلب XHR، لا يتم تمرير ملفات تعريف الارتباط مع الطلب. عندما تكون هناك حاجة لتمرير ملفات تعريف الارتباط، سيتعين عليك تعيين علامة على كائن XMLHttpRequest
.
const xhr = new XMLHttpRequest ( ) ;
const url = 'http://bar.com/api/v1/credentialed-content/' ;
function callOtherDomain ( ) {
if ( invocation ) {
xhr . open ( 'GET' , url , true ) ;
xhr . withCredentials = true ;
xhr . onreadystatechange = handler ;
xhr . send ( ) ;
}
}
طلب:
POST -d '{foo: bar}' /api/v1/post-here/
Host: bar.com
User-Agent: curl/7.69.1
Accept: */*
Origin: https://foo.com
X-PINGOTHER: pingpong
Content-Type: application/json;charset=UTF-8
Cookie: _session=NyV14oKXiS6HHopaf9nT
عند إجراء طلب XMLHttpRequest
وتعيين علامة بيانات withCredentials
، سيقوم متصفحك بتمرير Cookie
في رأس الطلب.
إجابة:
HTTP/1.1 200 OK
Connection: Keep-Alive
Content-Type: application/json
Access-Control-Allow-Origin: https://foo.com
Access-Control-Allow-Credentials: true
Cache-Control: no-cache
Pragma: no-cache
Set-Cookie: _session=AjBSqxj4T7bSySNTWeEm; expires=Wed, 31-05-2020 00:00:00 GMT
عندما يلاحظ متصفحك Access-Control-Allow-Credentials
مضبوط على true. سوف يحترم رأس Set-Cookie
ويحدد ملف تعريف الارتباط.
هام: لا ينبغي تعيين حرف البدل "*" في Access-Control-Allow-Origin
كما هو مذكور في قسم طلبات بيانات الاعتماد وأحرف البدل.
عند إجراء طلب بيانات الاعتماد عن طريق تعيين علامة withCredentials
، يجب تعيين Access-Control-Expose-Headers
بواسطة الخادم للسماح للمتصفح بمعرفة الرؤوس التي يمكن الوصول إليها.
في عالم ما قبل كورس، لا يمكن الوصول إلى رؤوس الاستجابة الافتراضية بواسطة المتصفح في طلب CORS. لذلك تم توضيحه بحيث يبحث المتصفح عن هذا الرأس لقراءة الرؤوس المكشوفة. بهذه الطريقة تتأكد مواصفات CORS من عدم تعطل المتصفحات القديمة.
يتم إرجاع هذا في طلبات ما قبل الرحلة. عندما يرى متصفحك هذا، يمكنه الوصول إلى رأس Set-Cookie
. كما ذكرنا أعلاه، في طلبات XHR العادية، لن يمرر متصفحك Cookie
في رأس الطلب وكذلك قراءة رأس استجابة Set-Cookie
.
بناء الجملة:
Access-Control-Allow-Credentials: true
يمكنك العثور على مثال في قسم الطلب ببيانات الاعتماد.
عندما نقول طلبًا معتمدًا، فهذا يعني ملفات تعريف الارتباط التي تم تمريرها في طلب XHR أو تعيينها عبر رأس الاستجابة Set-Cookie
. عندما يتم إجراء طلب XHR باستخدام علامة withCredentials، فإنك تأمل في تلقي الاستجابة بالإضافة إلى ملفات تعريف الارتباط التي سيتم تعيينها. ولكن، لا يمكنك أن تتوقع أن تكون Access-Control-Allow-Origin
هي "*" لأن ذلك يعني أن أي موقع ويب يمكنه استخدام ملفات تعريف الارتباط هذه. لهذا السبب، قد يفشل متصفحك في الطلب إذا رأى "*" في رأس الاستجابة Access-Control-Allow-Origin
.
عندما يستجيب طلب ما قبل الرحلة بالرقم 301/302، فقد لا تدعم بعض المتصفحات هذا حاليًا. قد تحصل على أخطاء مثل،
The request was redirected to 'https://example.com/foo', which is disallowed for cross-origin requests that require preflight
Request requires preflight, which is disallowed to follow cross-origin redirect
ملاحظة: للحصول على الحلول البديلة، تحقق من طلبات الاختبار المسبق وإعادة توجيه المستندات بواسطة Mozilla.
يحتوي المتصفح على إعدادات لرفض جميع ملفات تعريف الارتباط third-party
، عندما يقوم المستخدم بتمكين ذلك. على سبيل المثال، إذا تم تقديم طلب من https://foo.com
وكان الخادم موجودًا على https://bar.com
، فلن يقوم متصفحك بتعيين ملفات تعريف الارتباط المرسلة من https://bar.com
.