hi ...">

لماذا عناصر React لها خاصية $ typeof؟

حول آلية React لمنع إمكانية حقن JSON for XSS ، وتجنب نقاط الضعف الشائعة.


قد تعتقد أنك تكتب JSX:


<marquee bgcolor="#ffa7c4">hi</marquee> 

ولكن في الواقع كنت تتصل الوظيفة:


 React.createElement( /* type */ 'marquee', /* props */ { bgcolor: '#ffa7c4' }, /* children */ 'hi' ) 

وهذه الدالة تقوم بإرجاع كائن عادي يسمى عنصر React. وفقًا لذلك ، بعد اجتياز جميع المكونات ، يتم الحصول على شجرة كائنات مشابهة:


 { type: 'marquee', props: { bgcolor: '#ffa7c4', children: 'hi', }, key: null, ref: null, $$typeof: Symbol.for('react.element'), } 

إذا كنت قد استخدمت React من قبل ، فقد تكون معتادًا على حقول الكتابة والدعائم والمفتاح والمرجع. ولكن ما هي خاصية $$typeof ؟ ولماذا يكون لها الرمز Symbol() كقيمة له؟




قبل أن تصبح مكتبات واجهة المستخدم شائعة ، لعرض إدخال العميل في رمز التطبيق ، تم إنشاء سطر يحتوي على علامات HTML وإدراجه مباشرة في DOM ، عبر innerHTML:


 const messageEl = document.getElementById('message'); messageEl.innerHTML = '<p>' + message.text + '</p>'; 

تعمل هذه الآلية بشكل جيد ما لم يتم تعيين message.text على <img src onerror="stealYourPassword()"> . وفقًا لذلك ، نخلص إلى أنك لست بحاجة إلى تفسير جميع مدخلات العميل على أنها علامة HTML.


للحماية من مثل هذه الهجمات ، يمكنك استخدام واجهات برمجة التطبيقات الآمنة مثل document.createTextNode() أو textContent ، والتي لا تفسر النص. وكتدبير إضافي ، يمكنك الهروب من السلاسل عن طريق استبدال الأحرف التي يحتمل أن تكون خطرة مثل < ، > بأخرى آمنة.


ومع ذلك ، فإن احتمال الخطأ كبير ، لأنه من الصعب تتبع جميع الأماكن التي تستخدم فيها المعلومات التي سجلها المستخدم في صفحتك. لهذا السبب تعمل المكتبات الحديثة مثل React بأمان مع أي نص افتراضي:


 <p> {message.text} </p> 

إذا كانت message.text عبارة عن سلسلة ضارة بها علامة <img> ، فلن تتحول إلى علامة <img> حقيقية. React يهرب من محتوى النص ثم يضيفه إلى DOM. لذلك ، بدلاً من رؤية <img> ، يمكنك ببساطة رؤية ترميزها كسلسلة.


لعرض HTML التعسفي داخل عنصر React ، يجب عليك استخدام البناء التالي: dangerouslySetInnerHTML={{ __html: message.text }} . التصميم غير مريح عن قصد. بسبب العبث ، يصبح أكثر وضوحا ، ويجذب الانتباه عند عرض الكود.




هل هذا يعني أن React آمن تمامًا؟ رقم هناك العديد من أساليب الهجوم المعروفة استنادًا إلى HTML و DOM. سمات العلامة تستحق اهتماما خاصا. على سبيل المثال ، إذا كتبت <a href={user.website}> ، فيمكنك استبدال رمز ضار 'javascript: stealYourPassword()' نصية: 'javascript: stealYourPassword()' .


في معظم الحالات ، يكون وجود ثغرات أمنية من جانب العميل نتيجة لمشاكل على جانب الخادم ، ويجب إصلاحها أولاً وقبل كل شيء.


ومع ذلك ، فإن العرض الآمن لمحتوى النص المخصص هو خط الدفاع الأول المعقول الذي يعكس العديد من الهجمات المحتملة.


بناءً على الاعتبارات السابقة ، يمكننا أن نستنتج أن الشفرة التالية يجب أن تكون آمنة تمامًا:


 //   <p> {message.text} </p> 

ولكن هذا ليس هو الحال أيضا. وهنا نقترب من شرح وجود $$typeof في عنصر React.




كما أوضحنا سابقًا ، فإن عناصر React هي كائنات بسيطة:


 { type: 'marquee', props: { bgcolor: '#ffa7c4', children: 'hi', }, key: null, ref: null, $$typeof: Symbol.for('react.element'), } 

عادة ، يتم إنشاء عنصر React.createElement() عن طريق استدعاء وظيفة React.createElement() ، ولكن يمكنك إنشائها على الفور باستخدام حرفي ، كما فعلت أعلاه.


لنفترض أننا نخزن على الخادم سلسلة أرسلها المستخدم إلينا مسبقًا ، وفي كل مرة نعرضها على جانب العميل. لكن شخصًا ما ، بدلاً من الخيط ، أرسل إلينا JSON:


 let expectedTextButGotJSON = { type: 'div', props: { dangerouslySetInnerHTML: { __html: '/*     */' }, }, // ... }; let message = { text: expectedTextButGotJSON }; //    React 0.13 <p> {message.text} </p> 

وهذا هو ، فجأة ، بدلاً من السلسلة المتوقعة ، تحولت قيمة المتغير expectedTextButGotJSON إلى JSON. التي ستتم معالجتها بواسطة React كحرف ، وبالتالي تنفيذ التعليمات البرمجية الضارة.


يكون React 0.13 عرضة لهجوم يشبه XSS ، ولكن بدءًا من الإصدار 0.14 ، يتم تمييز كل عنصر برمز:


 { type: 'marquee', props: { bgcolor: '#ffa7c4', children: 'hi', }, key: null, ref: null, $$typeof: Symbol.for('react.element'), } 

تعمل هذه الحماية لأن الأحرف ليست قيمة JSON صالحة. لذلك ، حتى إذا كان لدى الخادم ثغرة أمنية محتملة وإرجاع JSON بدلاً من النص ، لا يمكن أن يحتوي JSON على Symbol.for('response.element') . يتحقق React من العنصر element.$$typeof ويرفض معالجة العنصر إذا كان مفقودًا أو غير صالح.


الميزة الرئيسية لـ Symbol.for() هي أن الرموز عالمية بين السياقات لأنها تستخدم سجل Symbol.for() . هذا يضمن نفس قيمة الإرجاع حتى في iframe. وحتى إذا كانت هناك عدة نسخ من React على الصفحة ، فستظل قادرة على "المطابقة" من خلال قيمة واحدة لـ $$typeof .




ماذا عن المتصفحات التي لا تدعم الشخصيات؟


للأسف ، لن يتمكنوا من تطبيق الحماية الإضافية التي تمت مناقشتها أعلاه ، لكن عناصر React ستظل تحتوي على خاصية $$typeof للتناسق ، لكنها ستكون مجرد رقم - 0xeac7 .


لماذا بالضبط 0xeac7 ؟ لأنه يبدو وكأنه رد فعل.

Source: https://habr.com/ru/post/ar432350/


All Articles