FlowingDev

الترميز المئوي، شرح مُبسّط: فك طلاسم %20 و %3F وغيرها من الرموز الهيروغليفية في الروابط

تعرّف على سبب عدم قدرة الروابط (URLs) على احتواء بعض المحارف، وكيف يقوم الترميز المئوي بترجمة المحارف غير الآمنة إلى صيغة آمنة ومفهومة عالميًا للويب.

جرّب الأداة: ترميز الروابط

بجملة واحدة

ترميز الروابط (URL encoding)، الذي يسمى رسميًا بالترميز المئوي (percent-encoding)، هو عملية ترجمة المحارف التي لها معنى خاص أو غير صالحة داخل الرابط إلى صيغة آمنة ومفهومة عالميًا حتى يمكن نقلها دون التسبب في أي ارتباك.

المشكلة التي يحلها

في حساء الويب البدائي في أيامه الأولى، كانت الحياة بسيطة. الروابط (URLs) — أو بشكل أعم، معرّفات الموارد الموحدة (URIs) — صُممت لتكون طريقة نظيفة ومتوقعة لتحديد مكان مورد ما. بنى المعماريون، ومن بينهم السير تيم بيرنرز-لي، هذا النظام على أساس مجموعة محدودة من المحارف: ASCII.

كان هذا يعمل بشكل رائع، طالما كنت تحتاج فقط للإشارة إلى http://example.com/reports/April.html. لكن ماذا يحدث عندما تصبح الأمور أكثر تعقيدًا؟

لنتأمل تشريح الرابط. له أجزاء: مخطط (http:)، مضيف (example.com)، مسار (/search)، وربما سلسلة استعلام (?q=dogs&cats). بعض المحارف تعمل كمخرجين هيكليين في هذه المسرحية. النقطتان الرأسيتان (:) تفصلان المخطط. الشرطة المائلة (/) تفصل أجزاء المسار. علامة الاستفهام (?) تبدأ معاملات الاستعلام. وعلامة العطف (&) تفصل بين معامل وآخر.

هنا تبدأ المشكلة. ماذا لو أردت البحث عن السلسلة الحرفية "C++ & C#‎"؟ إذا قمت بلصقها مباشرة في الرابط، ستحصل على .../search?q=C++ & C#. عندما يرى خادم الويب هذا، يصاب بارتباك شديد. يظن أن الاستعلام هو عن "C++ "، ثم يرى علامة العطف ويتوقع زوجًا آخر من مفتاح وقيمة، لكنه يجد فقط " C#‎" وحيدة. فوضى عارمة. المعنى المقصود قد ضاع.

علاوة على ذلك، بعض المحارف غير مسموح بها إطلاقًا. المسافة هي مشاغب كلاسيكي. متى تكون المسافة جزءًا من اسم ملف، ومتى تكون مجرد خطأ إملائي يجب على المتصفح تجاهله؟ وماذا عن المحارف خارج الأبجدية الإنجليزية الأساسية؟ الويب عالمي! كيف يمكنك وضع Résumé.pdf أو 你好.html في رابط مصمم لـ ASCII؟

الترميز المئوي يحل هذه الفئة من المشاكل بأكملها. إنه يوفر مخرج طوارئ، طريقة للقول، "يا سيد خادم الويب، المحرف(ات) التالية ليست هيكلية. لا تفسرها. إنها بيانات حرفية." إنه المترجم العالمي الذي يضمن أن الرابط يعني نفس الشيء في متصفح في البرازيل كما يعنيه على خادم في برلين.

كيف يعمل تحت الغطاء

"السحر" وراء الترميز المئوي بسيط بشكل مدهش. هو ليس خدعة سحرية بقدر ما هو شفرة استبدال بسيطة اتفق الجميع على استخدامها.

طاقم الممثلين: المحارف المحجوزة مقابل غير المحجوزة

أولاً، تحتاج إلى معرفة أي المحارف "كوول" وأيها تسبب المشاكل. تنقسم إلى بضع مجموعات.

نوع المحرف المحارف متى يجب الترميز
غير محجوز (Unreserved) A-Z a-z 0-9 - _ . ~ أبدًا. هذه هي الشخصيات الهامة (VIPs) في عالم الروابط. هي آمنة دائمًا.
محجوز (Reserved) : / ? # [ ] @ ! $ & ' ( ) * + , ; = أحيانًا. هذه المحارف لها معنى هيكلي خاص. إذا أردت استخدامها لمعناها (مثل / في مسار)، لا تقم بترميزها. إذا أردت استخدامها كـبيانات حرفية (مثل & في استعلام بحث)، يجب عليك ترميزها.
أخرى (غير آمنة) (مسافة), `< > " % { } \ ^` وجميع المحارف غير المنتمية لـ ASCII

الخلاصة الرئيسية هي السياق. المحرف ? لا بأس به إذا كان هو الـ? الوحيد الذي يفصل المسار عن سلسلة الاستعلام. ولكن إذا كنت بحاجة إلى علامة استفهام حرفية داخل قيمة معامل استعلام، يجب عليك ترميزها.

الخدعة السحرية: علامة النسبة المئوية + Hex

عملية الترميز هي رقصة بسيطة من ثلاث خطوات:

  1. اختر محرفًا تحتاج إلى ترميزه. لنستخدم علامة العطف &.
  2. ابحث عن قيمة البايت الخاصة به باستخدام مجموعة محارف قياسية. بالنسبة للويب، هذا المعيار هو UTF-8. في UTF-8 (وسلفه ASCII)، يُمثَّل المحرف & بالرقم العشري 38.
  3. حوّل هذا الرقم إلى رقم سداسي عشري (hexadecimal) من خانتين وأضف قبله علامة النسبة المئوية (%). الرقم العشري 38 هو 26 بالنظام السداسي العشري.

إذًا، & تصبح %26.

لنجرب المزيد:

  • المسافة هي الرقم العشري 32، والذي يعادل 20 بالنظام السداسي العشري. بعد الترميز: %20.
  • علامة الاستفهام (?) هي الرقم العشري 63، والذي يعادل 3F بالنظام السداسي العشري. بعد الترميز: %3F.
  • علامة النسبة المئوية (%) نفسها هي الرقم العشري 37، أي 25 بالنظام السداسي العشري. لذا لترميز علامة % حرفية، تكتب %25.

هذا النظام عبقري لأن علامة النسبة المئوية نفسها ليست من المحارف غير المحجوزة، لذا يعرف المحلل (parser) أنه كلما رأى علامة %، يجب أن يتوقع وجود خانتين سداسيتي العشرات بعدها.

ماذا عن المحارف غير الإنجليزية؟

هنا يصبح UTF-8 حاسمًا. محرف ASCII بسيط مثل A هو بايت واحد. لكن محرفًا مثل é الفرنسية أو 好 الصينية يُمثَّل بعدة بايتات في UTF-8. عملية الترميز هي نفسها، فقط تتكرر لكل بايت.

لنأخذ é كمثال:

  1. في UTF-8، تُمثَّل é بـبايتين: C3 و A9 (بالنظام السداسي العشري).
  2. ترميز كل بايت على حدة:
    • C3 تصبح %C3.
    • A9 تصبح %A9.
  3. اجمعهما: é تصبح %C3%A9.

عملية فك الترميز هي العكس تمامًا. يرى المتصفح أو الخادم %C3%A9، يأخذ البايتين C3 و A9، يمررهما عبر مفكك ترميز UTF-8، ويستعيد المحرف الجميل é.

قصص من الواقع

النظرية رائعة، لكن دعنا نرى أين يلامس الواقع النظري.

قضية استعلام البحث المختفي

مایا، مطورة مبتدئة، كانت تبني ميزة بحث لموقع توثيق تقني. كان بإمكان المستخدمين البحث عن أشياء مثل "C++‎" و "promises & async/await" وما إلى ذلك. قامت ببناء رابط البحث ببساطة عن طريق ربط السلاسل النصية: site.com/search?q= + userInput.

ساءت الأمور. البحث عن promises & async/await أنتج الرابط .../search?q=promises & async/await. لكن الخادم أبلغ فقط عن مصطلح البحث "promises ". تم تفسير & كفاصل لمعامل جديد، async/await، والذي تم تجاهله لأنه لم يكن له مفتاح. كانت نتائج بحثها خاطئة تمامًا.

الدرس: تعلمت مايا قاعدة أساسية في تطوير الويب: قم دائمًا بترميز أي بيانات ديناميكية توضع في مكون من مكونات الرابط ترميزًا مئويًا. بعد أن بدأت في ترميز مدخلات المستخدم، أصبح الرابط الصحيح .../search?q=promises%20%26%20async%2Fawait. الآن تلقى الخادم السلسلة الكاملة والصحيحة، وعمل البحث بشكل مثالي.

الحادثة الدولية

قرر متجر إلكتروني عرض منتج جديد من شريك ألماني: "Fußball". أنشأ فريق التسويق رابطًا لطيفًا له: store.com/products/Fußball. على متصفحاتهم الحديثة في المكتب، بدا كل شيء على ما يرام.

لكن يوم الإطلاق كان فوضويًا. غمرت تذاكر دعم العملاء النظام. بعض المستخدمين كانوا يتلقون أخطاء "404 Not Found". آخرون رأوا رابطًا يبدو مثل .../products/Fu%C3%9Fball في شريط المتصفح، بينما رأى البعض .../products/FuÃball. كان النظام خليطًا من المكونات القديمة والجديدة، ولم تكن تتعامل مع الحرف غير المنتمي لـ ASCII وهو ß (Eszett) بشكل متسق. بعض الأجزاء لم تقم بترميزه، وبعضها قام بترميزه بافتراض UTF-8، وبعض الأنظمة القديمة قامت بفك ترميزه بافتراض مجموعة محارف مختلفة، مما أدى إلى ظهور رموز غير مفهومة (mojibake).

الدرس: الاعتماد على المتصفحات والخوادم لـ"مجرد التعامل" مع المحارف غير المنتمية لـ ASCII في الروابط هو وصفة لعدم التناسق. الترميز المئوي الاستباقي والمتسق لجميع المحارف غير المحجوزة باستخدام معيار UTF-8 يضمن أن روابطك قوية وتعمل بشكل متوقع عبر منظومة الويب بأكملها، القديمة والجديدة.

ورطة الترميز المزدوج

كان فريق يبني نظام تسجيل دخول موحد (SSO). كان التدفق يعمل هكذا: service-a.com يعيد توجيه المستخدم إلى sso.com/login، ويمرر رابطه الخاص كمعامل حتى يمكن إعادة المستخدم بعد تسجيل الدخول. كان رابط إعادة التوجيه يبدو هكذا: sso.com/login?redirect_uri=https://service-a.com/dashboard?param=1.

المطور في service-a.com كان ذكيًا وقام بترميز قيمة redirect_uri، مما أنتج: sso.com/login?redirect_uri=https%3A%2F%2Fservice-a.com%2Fdashboard%3Fparam%3D1.

لكن إطار العمل الذي استخدموه كان يحتوي على طبقة برمجية وسيطة (middleware) تقوم، "لأسباب أمنية"، بترميز جميع معاملات الاستعلام الصادرة تلقائيًا. رأت هذه الطبقة السلسلة المرمزة بالفعل وقامت بترميزها مرة أخرى. تم تحويل % في %3A إلى %25، لذا أصبحت %3A هي %253A. كان الرابط النهائي عبارة عن فوضى مشوهة من الترميز المزدوج. عندما وصل المستخدم إلى sso.com، قام بفك ترميز الرابط مرة واحدة وحصل على السلسلة المرمزة مرة واحدة، والتي لم يتمكن من استخدامها لإعادة التوجيه، مما أدى إلى كسر تدفق تسجيل الدخول بالكامل.

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

أخطاء وفخاخ شائعة

  • ترميز الرابط بأكمله. لا تفعل هذا أبدًا. إذا قمت بترميز https://example.com، ستحصل على شيء مثل https%3A%2F%2Fexample.com. هذا لم يعد رابطًا صالحًا؛ أصبحت أجزاء المخطط والمضيف الآن مجرد خليط لا معنى له من المحارف. يجب عليك فقط ترميز المكونات الفردية التي تحتاج إليه (مثل قيم معاملات الاستعلام أو أجزاء مسار معينة).
  • عدم الترميز على الإطلاق. الخطيئة الأكثر شيوعًا. حشر مدخلات المستخدم الخام أو البيانات ذات المحارف الخاصة مباشرة في سلسلة الرابط هو طلب مباشر للثغرات الأمنية (مثل Cross-Site Scripting) والوظائف المعطلة.
  • نسيان السياق. المحرف & لا بأس به في مسار الرابط، لكنه فاصل محجوز في سلسلة الاستعلام. الشيء نفسه ينطبق على /. لا تحتاج إلى ترميز المحارف المحجوزة عند استخدامها لغرضها الخاص.
  • الخلط بين + و %20. في نوع المحتوى application/x-www-form-urlencoded (المستخدم بواسطة نماذج HTML)، غالبًا ما يتم ترميز المسافات كعلامة + في سلسلة الاستعلام. بينما يفهم العديد من الخوادم هذا، فإن الترميز المئوي الرسمي للمسافة هو %20. استخدام %20 لا لبس فيه ويعمل بشكل صحيح في جميع أجزاء الرابط، وليس فقط في سلسلة الاستعلام. عندما تكون في شك، التزم بـ %20.
  • استخدام مجموعة محارف قديمة. الويب يعمل على UTF-8. إذا قمت بترميز بياناتك باستخدام مجموعة محارف مختلفة (مثل ISO-8859-1)، فإن الخادم الذي يتوقع UTF-8 سيسيء تفسير البايتات ويشوه بياناتك. حدد دائمًا واستخدم UTF-8.

لماذا يجب أن تضعه في حسبانك

إذا كنت تكتب كودًا يمس رابطًا، فأنت بحاجة إلى فهم الترميز المئوي. إنه ليس اختياريًا. يجب أن تفكر فيه كلما كنت:

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

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

للمزيد من التعمق

  • RFC 3986: المواصفات الأساسية لمعرف الموارد الموحد (URI). يحدد القسم 2 مجموعة المحارف وقواعد الترميز المئوي. إنه المصدر النهائي للحقيقة.
  • MDN Web Docs: encodeURIComponent(): دليل عملي لمطوري JavaScript، يشرح أي دالة يجب استخدامها ولماذا. يربط قسم "See also" بدوال ترميز أخرى ذات صلة.
  • Wikipedia: Percent-encoding: نظرة عامة شاملة وسهلة القراءة للمفهوم وتاريخه والفروق الدقيقة المختلفة فيه.
  • W3C: Character encodings: مقدمة عالية المستوى لسبب أهمية ترميزات المحارف على الويب، مع UTF-8 كبطل القصة.

انتهينا من النظرية. حان وقت التطبيق — 100% في متصفحك.

جرّب الأداة: ترميز الروابط