مُرمِّز ومُفكِّك كيانات HTML

اجعل النص آمنًا داخل HTML عبر الهروب، أو فُكّ الكيانات المسماة والرقمية لتعود إلى المحارف التي تمثلها.

نص
مُرمَّز

سيظهر الإخراج هنا

ما الذي تفعله هذه الأداة

لا تملك HTML وسيلة للتمييز بين علامة «أصغر من» قصدتها نصًا وأخرى تفتح وسمًا. تحل اللغة ذلك بالكيانات: متتاليات هروب قصيرة تمثل محرفًا دون أن تُقرأ وسمًا. تحوّل هذه الأداة في الاتجاهين — من النص العادي إلى صورته الهاربة، ومن HTML الهارب إلى المحارف التي يمثلها.

يجري الاتجاهان كلاهما داخل متصفحك بالكامل. وهذا هنا أهم منه في معظم الأدوات، لأن النص الذي يُلصق في مفكك كيانات يكون عادةً صفحة مسحوبة بمكشطة، أو بريد عميل، أو عمودًا في قاعدة بيانات يحاول أحدهم فهمه.

المحارف الخمسة التي تهم

توجد آلاف الكيانات، لكن حفنة منها تكفي لجعل النص آمنًا. الترميز هنا يستبدل خمسة محارف بالضبط ويترك كل ما عداها مقروءًا:

  • العطف يصير & — هذا أولًا، وإلا لأُعيدت قراءة كل استبدال آخر بوصفه كيانًا.
  • علامة «أصغر من» تصير < — المحرف القادر على فتح وسم.
  • علامة «أكبر من» تصير > — أقل خطرًا بمفردها، وتُهرَّب للتناظر ولتصمد أمام المحللات المتساهلة.
  • علامة التنصيص المزدوجة تصير " — لازمة داخل قيم السمات المكتوبة بتنصيص مزدوج.
  • الفاصلة العليا تصير ' — لازمة داخل قيم السمات المكتوبة بتنصيص مفرد.

الأخيرة مكتوبة رقميًا عن قصد. لها اسم، '، لكنه جاء من XML ولم تعرّفه HTML 4 قط، فتمرره المحللات القديمة نصًا حرفيًا ويفشل الهروب في صمت. أما الصورة الرقمية فقد عملت دائمًا في كل مكان، ولهذا تُخرجها مكتبات الهروب الجادة.

المسماة والعشرية والست عشرية

يمكن كتابة أي محرف بثلاث طرائق، والفك يقبلها جميعًا. الشرطة الطويلة مثال جيد:

named     —      الصورة المقروءة، لكن للمحارف التي لها اسم فقط
decimal   —      نقطة الترميز بالأساس 10
hex       —     نقطة الترميز نفسها بالأساس 16، وهي كتابة جداول يونيكود

تعرف هذه الأداة عند الفك 255 اسمًا من مجموعة HTML 4 — الحروف اللاتينية والأبجدية اليونانية والأسهم والرموز الرياضية وعلامات الترقيم الطباعية، وهي تغطي مجتمعةً كل ما يظهر عمليًا في النص الحقيقي. وتعرّف HTML 5 نحو 2200 اسم إضافي، أغلبها رموز رياضية نادرة؛ وحمل ذلك الجدول يضيف عشرات الكيلوبايتات إلى كل تحميل صفحة مقابل أسماء لا تستعملها معظم المستندات أبدًا. والاسم الخارج عن المجموعة يُبلَّغ عنه ولا يُحذف، فالثمن تنبيه لا إجابة خاطئة.

ويسير الترميز في الاتجاه المعاكس ويبقى بسيطًا عن قصد: أسماء للمحارف الخمسة أعلاه، وأرقام عشرية لكل ما عداها. ولا شيء يتحول إلى … أو ’ ما دام المحرف نفسه صالحًا تمامًا في مستند UTF-8.

الهروب ليس قاعدة واحدة بل أربعًا

يغري المرء أن يعد هروب الكيانات جوابًا على الحقن، وهو داخل محتوى العنصر كذلك فعلًا. لكن HTML في حقيقتها أربع لغات متداخلة، لكل منها قواعدها. وهروب الكيانات صحيح في أول موضعين مما يلي وعديم الفائدة في الباقي:

  • محتوى العنصر — النص بين الوسوم. هروب الكيانات هو المطلوب بالضبط.
  • قيم السمات بين علامات تنصيص. هروب الكيانات صحيح، شرط أن تكون السمة فعلًا بين علامات تنصيص؛ فبدونها تُنهي مسافةٌ في القيمة السمةَ ولا يفيدك الهروب شيئًا.
  • داخل كتلة script. تسري قواعد هروب سلاسل JavaScript لا الكيانات — فالمتصفح لا يفك الكيانات هناك، فتصل علامة التنصيص الهاربة بوصفها المحارف الحرفية ".
  • سمات العناوين مثل href و‑src. لا يوقف هروب الكيانات عنوانًا من نوع javascript:، لأن الخطر في المخطط لا في محرف كان الكيان ليستبدله.

القاعدة العملية: هرّب عند النقطة التي يُدرَج فيها النص في المستند، وفق قواعد السياق الدقيق الذي يحط فيه — لا مرة واحدة مبكرًا على أمل أن يصمد ذلك في كل مكان لاحقًا.

لماذا تظهر أحيانًا كتابة مضاعفة

هذا أشيع سبب على الإطلاق للجوء إلى مفكك كيانات. هُرِّب النص ثم هُرِّب مرة أخرى — عادةً مرة من شيفرة التطبيق ومرة من محرك قوالب أو إطار عمل يهرب مخرجاته أصلًا:

original     Fish & Chips
escaped      Fish & Chips
escaped x2   Fish & Chips

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

ما بعد ASCII والخيار الخاص به

الصفحة المقدَّمة بترميز UTF-8 لا تحتاج كيانات للحروف المشكولة ولا للعبرية والعربية والصينية والرموز التعبيرية. هذه المحارف صالحة كما هي، وإبقاؤها مقروءة يبقي المصدر مقروءًا والملف أصغر. وهذا هو المبدئي هنا.

ويوجد الخيار لأن بعض الوجهات ما زالت تصر على ASCII وحده: قوالب بريد قديمة، أو أنظمة تفسد البايتات فوق 127، أو عمود في قاعدة بيانات لا يرضى أحد بتغيير ترميزه. وتفعيله يكتب كل محرف من هذا النوع مرجعًا عشريًا: é تصير é، وא تصير א.

وتستحق الرموز التعبيرية ملاحظة. يُكتب الرمز التعبيري مرجعًا واحدًا لنقطة ترميزه كاملة، مثل 🙂، ولا يُكتب نصفين أبدًا. وإن كنت استعملت أداة هروب JSON فهذا عكس ما يجري هناك، حيث يشترط الشكل صراحةً تقسيم الرمز التعبيري إلى متتاليتَي هروب \u. المحرف نفسه وجوابان صحيحان، لأن الشكلين يعرّفان الهروب فوق وحدات مختلفة.

حين يتعذر فك مرجع

الفك هنا لا يفشل أبدًا ولا يحذف شيئًا. والمرجع الذي يتعذر حله يبقى تمامًا كما وُجد ويُدرج فوق الخرج مع سطره وعموده. ويُبلَّغ عن ثلاثة أمور:

  • اسم غير معروف مثل &nbps; — وهو في الغالب الأعم خطأ إملائي عن  . وهذا هو التنبيه الجدير بالتصرف: معناه أن مسافة ناقصة من محتواك ولم ينتبه أحد.
  • رقم ليس محرفًا، مثل � أو قيمة فوق مدى يونيكود. لا شيء يُفَك إليه، فيبقى نصًا.
  • رقم بين 128 و‑159. انظر أدناه.

أما علامة العطف المفردة فلا يُبلَّغ عنها إطلاقًا. ففي HTML تُعد R&D و‑Q&A نصًا عاديًا، ومعاملة كل علامة عطف شاردة بوصفها خطأً كانت ستدفن التنبيهات المهمة تحت ضجيج النثر.

والمدى من 128 إلى 159 غرابة حقيقية. فهذه الأرقام تسمي محارف تحكم غير مرئية لا يقصدها أحد قط؛ وإنما تظهر لأن المستند حُرِّر بترميز Windows-1252، حيث الأرقام نفسها هي علامات تنصيص مقوسة وشرطات ورمز اليورو. والمتصفحات تقرؤها بهدوء بوصفها Windows-1252 لهذا السبب بعينه، وتفعل الأداة مثلها كي تساوي النتيجةُ ما ستعرضه الصفحة فعلًا — لكنها تقول ذلك في التنبيهات، لأن استبدالًا صامتًا لم تطلبه أسوأ من استبدال معلن.

الأسئلة الشائعة

هل يكفي هروب هذه المحارف الخمسة لإيقاف XSS؟
في محتوى العنصر وفي قيم السمات بين علامات تنصيص، نعم، فهذا غرضه بالضبط. ولا يكفي داخل كتلة script أو style، ولا في سمة بلا علامات تنصيص، ولا في سمة عنوان مثل href، إذ يحتاج كل موضع منها قواعده الخاصة. الهروب خاصية المكان الذي يحط فيه النص لا خاصية النص.
لماذا تُرمَّز الفاصلة العليا ' لا '؟
لأن ' كيان XML لم تعرّفه HTML 4 قط. والمحللات السابقة لـ HTML 5 تتركه نصًا حرفيًا، فتكون فاصلة عليا حسبتها هاربة وهي ليست كذلك. أما الصورة الرقمية فتعمل في كل محلل، ولذلك هي المبدئي الآمن.
صفحتي تعرض كيانًا مضاعفًا بدل علامة العطف. ماذا جرى؟
هُرِّب النص مرتين. فُكّه هنا: تمريرة واحدة تعيد الكتابة المضاعفة إلى المفردة وتؤكد وجود طبقة زائدة. أصلح مسار المعالجة لا النص — فغالبًا تهرب شيفرتك مخرجًا كان محرك القوالب يهربه أصلًا.
هل أحتاج إلى ترميز العبرية أو العربية أو الرموز التعبيرية؟
ليس في صفحة حديثة مقدَّمة بترميز UTF-8؛ فهي صالحة كما هي. فعِّل خيار ما بعد ASCII فقط حين يشترط شيء لاحق في المسار ASCII وحده، مثل قالب بريد قديم. وعندها يُكتب كل رمز تعبيري مرجعًا رقميًا واحدًا.
لماذا تنبه الأداة على “ مع أنها فكته على نحو سليم؟
لأن 147 يسمي محرف تحكم غير مرئي لا علامة التنصيص التي تراها. والمستندات التي تستعمله كُتبت بترميز Windows-1252، حيث 147 علامة تنصيص مقوسة: تقرؤها المتصفحات هكذا، وتقرؤها الأداة كذلك. والتنبيه يخبرك بأن استبدالًا وقع، كي تصحح ترميز المصدر إن كان بيدك.
هل يُرسَل نصي إلى أي مكان؟
لا. الترميز والفك يجريان داخل متصفحك بالكامل؛ ولا يغادر جهازك شيء مما تلصقه.