لماذا يحتاج HTML "أمانًا" أصلًا؟
HTML نفسه لا يشغّل كودًا خطيرًا وحده، لكن طريقة استخدامه تفتح ثغرات حقيقية — خصوصًا حين تربط صفحتك بمصادر خارجية لا تتحكم بها: رابط لموقع آخر، إطار <iframe> مضمّن، أو حتى نص كتبه مستخدم آخر وعرضته على صفحتك. هذا الدرس يبني على الروابط والصور والوسائط المتعددة.
target="_blank" بحاجة إلى rel="noopener"
<!-- ⚠️ الرابط أدناه ثغرة أمنية معروفة -->
<a href="https://external-site.com" target="_blank">رابط خارجي</a>
<!-- ✅ الصيغة الآمنة -->
<a href="https://external-site.com" target="_blank" rel="noopener noreferrer">رابط خارجي</a>
حين يفتح رابط بـ target="_blank" بلا rel="noopener"، تحصل الصفحة الجديدة على وصول جزئي لكائن window الخاص بصفحتك الأصلية عبر window.opener — ويمكن نظريًا لصفحة خبيثة استغلال هذا لإعادة توجيه صفحتك الأصلية لموقع تصيّد (Tabnabbing) بينما أنت لا تزال تتصفح التبويب الجديد دون أن تلاحظ.
rel="noopener"يقطع هذا الوصول تمامًا.rel="noreferrer"يضيف فوقها: لا يرسل ترويسةReferer(عنوان الصفحة التي جئت منها) للموقع الوجهة.- المتصفحات الحديثة تطبّق سلوك
noopenerتلقائيًا معtarget="_blank"، لكن كتابته صراحة تبقى أفضل ممارسة موثّقة ومتوافقة مع كل الإصدارات.
🔑 قاعدة سريعة: أي
target="_blank"تكتبه إلى رابط خارجي، أرفقه بـrel="noopener noreferrer"دون تفكير إضافي.
تضمين محتوى لا تثق به تمامًا: <iframe sandbox>
في درس الوسائط رأينا <iframe> لتضمين YouTube وخرائط. حين يكون مصدر المحتوى المضمّن أقل ثقة — إعلان من طرف ثالث، أو معاينة كود يكتبها مستخدمك — استخدم خاصية sandbox:
<iframe
src="https://untrusted-widget.example.com"
sandbox="allow-scripts"
title="ودجت خارجي">
</iframe>
sandbox (بلا أي قيمة) يفرض أقصى تقييد ممكن: لا سكربتات، لا نماذج، لا نوافذ منبثقة، ومصدر معزول لوحده (origin فريد لا يشارك تخزينًا مع موقعك). تضيف بعدها فقط ما تحتاجه فعلًا كقيم مفصولة بمسافة:
| القيمة | ما تسمح به |
|---|---|
allow-scripts | تشغيل جافاسكريبت داخل الإطار |
allow-forms | إرسال نماذج من داخل الإطار |
allow-popups | فتح نوافذ/تبويبات جديدة من الإطار |
allow-same-origin | معاملة الإطار كأنه من نفس مصدر صفحتك (احذرها مع محتوى لا تثق به) |
خاصية أخرى مفيدة هي referrerpolicy="no-referrer" لمنع إرسال عنوان صفحتك للموقع المضمّن.
⚠️ لا تجمع أبدًا
allow-scriptsمعallow-same-originعلى محتوى لا تثق به — الجمع بينهما يسمح للسكربت المضمّن بالتلاعب بصفحتك عبر DOM، وهو بالضبط ما يفترض أن يمنعهsandbox(راجع مرجع وسمiframe).
أخطر ثغرة تصنعها بنفسك: حقن HTML من مدخلات غير موثوقة
هذه أهم نقطة في الدرس. تخيّل صفحة تعليقات تعرض تعليق كل زائر مباشرة داخل الصفحة:
<!-- ❌ خطر: عرض نص المستخدم كـ HTML خام دون تنقية -->
<div class="comment"><!-- محتوى التعليق يُلصق هنا كما هو --></div>
لو كتب أحد الزوار تعليقًا يحتوي <script> أو معالج حدث مثل onerror، وعُرض نصّه كـ HTML خام بدلًا من نص عادي، سينفّذه متصفح كل زائر آخر يفتح الصفحة. هذا هجوم يُسمّى Cross-Site Scripting (XSS)، وهو من أكثر ثغرات الويب شيوعًا وخطورة — لكن يبدأ غالبًا من قرار HTML بسيط: "هل أعرض هذا النص كنص، أم كـ HTML؟"
القاعدة التي يوفّرها HTML/DOM نفسه:
- عرض نص كمحتوى نصي عادي (
textContentفي جافاسكريبت، أو محرك القوالب الذي يهرّب الرموز تلقائيًا افتراضيًا) آمن دائمًا — أي<أو>يظهر كرمز حرفي، لا كوسم. - عرض نص كـ HTML خام (
innerHTMLفي جافاسكريبت، أو أي دالة تسمّى صراحة "raw"/"unsafe"/"dangerouslySet...") يُنفَّذ ككود — استخدمه فقط لمحتوى تثق به 100%، أبدًا لمدخلات مستخدم مباشرة.
⚠️ HTML وحده لا يملك آلية "تنقية" — الحماية الفعلية تحدث في طبقة الخادم أو الإطار البرمجي الذي يبني الصفحة (تهريب الرموز الخاصة، أو قوائم سماح Sanitization). ما يمنحك إياه HTML هو فهم أين يكمن الخطر: أي مكان تُعامَل فيه مدخلات المستخدم كـ HTML بدل نص.
الاستخدام الواقعي
- موقع تجارة إلكترونية يضع أزرار "شارك على" لمواقع خارجية بـ
target="_blank"— يحتاجrel="noopener noreferrer"على كل واحد. - لوحة تحكّم تعرض معاينة كود HTML كتبه المستخدم (مثل محرر الأكواد في هذا الموقع) — تُعرض داخل
<iframe sandbox>معزول، لا كـ HTML مباشر في الصفحة الرئيسية. - منتدى أو قسم تعليقات — نص التعليق يُعرض كنص مُهرَّب (escaped) دائمًا، لا كـ HTML خام.
الأخطاء الشائعة
- ❌
target="_blank"بلاrel="noopener"على روابط خارجية — يبقى أشهر خطأ أمني بسيط في HTML. - ❌ الظن أن
sandboxوحدها كافية بلا التفكير في القيم التي تضيفها فوقها —sandboxالفارغة مقيّدة جدًا وقد تكسر الودجت المضمّن إن لم تُرفق القيم الصحيحة. - ❌ عرض مدخلات المستخدم كـ HTML خام (
innerHTMLأو ما يعادلها) دون تفكير — أشهر سبب لثغرات XSS الحقيقية في الإنتاج. - ❌ افتراض أن HTML "آمن دائمًا" لأنه ليس لغة برمجة — القرارات حول كيفية عرض المحتوى الخارجي هي بالضبط ما يحدد الأمان.
خلاصة
كل رابط target="_blank" لموقع خارجي يحتاج rel="noopener noreferrer". كل <iframe> لمحتوى لا تثق به بالكامل يحتاج sandbox بأقل الصلاحيات الكافية لعمله. والقاعدة الأهم: نص المستخدم يُعرض كنص، لا كـ HTML خام، إلا إذا كنت تثق بمصدره تمامًا — فهذا هو الفرق بين صفحة عادية وصفحة قابلة للاختراق.