أمان الذاكرة للخطوط على الويب

Dominik Röttsches
Dominik Röttsches
Rod Sheeter
Rod Sheeter
Chad Brokaw
Chad Brokaw

تاريخ النشر: 19 مارس 2025

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

توضّح هذه المشاركة سبب توقّف Chrome عن استخدام FreeType، وتقدّم بعض التفاصيل الفنية المثيرة للاهتمام حول التحسينات التي أتاحها هذا التغيير.

لماذا تم استبدال FreeType؟

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

يتضمّن Chrome مكتبة FreeType ويستخدمها كمكتبة أساسية لمعالجة الخطوط على Android وChromeOS وLinux. وهذا يعني أنّ الكثير من المستخدمين سيتأثرون في حال وجود ثغرة أمنية في FreeType.

يستخدم Chrome مكتبة FreeType لاحتساب المقاييس وتحميل المخططات التفصيلية التي تم تلميحها من الخطوط. بشكل عام، كان استخدام FreeType مفيدًا جدًا لشركة Google. وهو يؤدي مهمة معقّدة بشكل جيد، ونعتمد عليه بشكل كبير ونساهم في تطويره. ومع ذلك، تمت كتابته باستخدام رمز برمجي غير آمن، ويعود تاريخه إلى فترة لم يكن فيها من المرجّح أن يتم إدخال بيانات ضارة. إنّ مجرد مواكبة سيل المشاكل التي يتم العثور عليها من خلال التشويش يكلف Google ما لا يقل عن 0.25 من مهندسي البرامج بدوام كامل. والأسوأ من ذلك، أنّنا لا نعثر على كل المشاكل أو نعثر عليها فقط بعد أن يتم طرح الرمز البرمجي للمستخدمين.

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

لماذا تستمر المشاكل في الظهور؟

عند تقييم أمان FreeType، لاحظنا حدوث ثلاث فئات رئيسية من المشاكل (غير شاملة):

استخدام لغة غير آمنة

النمط/المشكلة مثال
إدارة الذاكرة يدويًا
الوصول إلى مصفوفة بدون التحقّق CVE-2022-27404
تجاوز سعة الأعداد الصحيحة أثناء تنفيذ الأجهزة الافتراضية المضمّنة لتلميح TrueType لرسم CFF وتلميحه
https://issues.oss-fuzz.com/issues?q=FreeType%20Integer-overflow
الاستخدام غير الصحيح للتخصيص الذي يتم فيه ضبط القيمة على صفر مقابل التخصيص الذي لا يتم فيه ضبط القيمة على صفر المناقشة في https://gitlab.freedesktop.org/freetype/freetype/-/merge_requests/94، تم العثور على 8 مشاكل في أداة فحص الأخطاء بعد ذلك
عمليات تحويل غير صالحة الاطّلاع على الصف التالي بشأن استخدام وحدات الماكرو

المشاكل الخاصة بالمشروع

النمط/المشكلة مثال
تخفي وحدات الماكرو عدم توفّر كتابة واضحة للحجم
  • تخفي وحدات الماكرو، مثل FT_READ_* وFT_PEEK_*، أنواع الأعداد الصحيحة المستخدَمة، ما يمنع ظهور أنواع C99 ذات الأحجام الواضحة (int16_t وما إلى ذلك) التي لا يتم استخدامها.
تؤدي إضافة رمز جديد باستمرار إلى حدوث أخطاء، حتى عند كتابة الرمز بشكل دفاعي.
  • يتوافق كل من COLRv1 وOT-SVG مع المشكلتين اللتين تم رصدهما
  • تساعد عملية التشويش في العثور على بعض الأخطاء، ولكن ليس بالضرورة على جميعها #32421، #52404
عدم توفّر اختبارات
  • يستغرق تصميم خطوط تجريبية وقتًا طويلاً ويصعب تنفيذه

مشاكل التبعية

وقد رصدت عملية التشويش بشكل متكرّر مشاكل في المكتبات التي تعتمد عليها FreeType، مثل bzip2 وlibpng وzlib. على سبيل المثال، قارِن freetype_bdf_fuzzer: Use-of-uninitialized-value in inflate.

لا يكفي إجراء اختبار التشويش

يهدف الاختبار الغامض، وهو اختبار مبرمَج باستخدام مجموعة كبيرة من المدخلات، بما في ذلك المدخلات العشوائية غير الصالحة، إلى العثور على العديد من أنواع المشاكل التي تظهر في الإصدار الثابت من Chrome. نستخدم اختبار التشويش في FreeType كجزء من مشروع oss-fuzz من Google. على الرغم من أنّ هذه الطريقة تنجح في العثور على المشاكل، إلا أنّ الخطوط أثبتت مقاومتها إلى حد ما للتجربة العشوائية، وذلك للأسباب التالية.

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

  • البيانات الوصفية الثابتة، مثل أسماء الخطوط ومَعلمات الخطوط المتغيرة
  • عمليات الربط بين أحرف Unicode والرسومات.
  • مجموعة قواعد ونحو معقّدان لتنسيق الرموز الرسومية على الشاشة
  • المعلومات المرئية: أشكال الرموز الرسومية ومعلومات الصور التي تصف شكل الرموز الرسومية المعروضة على الشاشة.
    • يمكن أن تتضمّن الجداول المرئية بدورها برامج TrueType لإجراء تعديل، وهي برامج صغيرة يتم تنفيذها لتغيير شكل الحرف الرسومي.
    • سلاسل الأحرف في جداول CFF أو CFF2 التي تمثّل تعليمات إلزامية لرسم المنحنيات وتحسينها، ويتم تنفيذها في محرك عرض CFF.

تتسم ملفات الخطوط بالتعقيد، فهي تتضمّن لغة برمجة خاصة بها ومعالجة آلة الحالة، ما يتطلّب أجهزة افتراضية محدّدة لتنفيذها.

بسبب تعقيد التنسيق، يواجه اختبار التشويش بعض المشاكل في العثور على أخطاء في ملفات الخطوط.

يصعب تحقيق نسبة استخدام رموز الصفحة أو إحراز تقدّم في اختبار عدم التوافق للأسباب التالية:

  • تواجه برامج التلميح في TrueType وسلاسل أحرف CFF وتصميم OpenType التي تستخدم أدوات تغيير بسيطة للبتات/التحويل/الإدراج/الحذف صعوبة في الوصول إلى جميع مجموعات الحالات.
  • يجب أن ينتج التشويش بنى صالحة جزئيًا على الأقل. ونادرًا ما تؤدي الطفرات العشوائية إلى ذلك، ما يصعّب تحقيق تغطية جيدة، لا سيما بالنسبة إلى المستويات الأعمق من الرمز.
  • لا تستخدم جهود التشويش الحالية في ClusterFuzz وoss-fuzz بعد التغيير المراعي للبنية. قد يساعد استخدام أدوات التعديل التي تراعي القواعد النحوية أو البنية في تجنُّب إنشاء صيغ يتم رفضها مبكرًا، ولكن على حساب استغراق وقت أطول في التطوير، وإدخال فرص تفوت أجزاءً من مساحة البحث.

يجب أن تكون البيانات في جداول متعدّدة متزامنة لكي يتمكّن التشويش من إحراز تقدّم:

  • لا تنتج أنماط التغيير المعتادة في أدوات التشويش بيانات صالحة جزئيًا، لذا يتم رفض العديد من التكرارات ويصبح التقدّم بطيئًا.
  • إنّ ربط الحروف الرسومية وجداول تنسيق OpenType ورسم الحروف الرسومية مرتبطة ببعضها وتعتمد على بعضها البعض، ما يشكّل مساحة توافقية يصعب الوصول إلى زواياها باستخدام التشويش.
  • على سبيل المثال، استغرق العثور على ثغرة tt_face_get_paint COLRv1 الأمنية ذات الخطورة العالية أكثر من 10 أشهر.

على الرغم من بذل قصارى جهدنا، وصلت مشاكل أمان الخطوط بشكل متكرر إلى المستخدمين النهائيين. سيؤدي استبدال FreeType ببديل مكتوب بلغة Rust إلى منع العديد من فئات الثغرات الأمنية بالكامل.

Skrifa in Chrome

Skia هي مكتبة الرسومات المستخدَمة في Chrome. تعتمد Skia على FreeType لتحميل البيانات الوصفية وأشكال الحروف من الخطوط. Skrifa هي مكتبة Rust، وهي جزء من مجموعة مكتبات Fontations، وتوفّر بديلاً آمنًا لأجزاء FreeType التي تستخدمها Skia.

لنقل FreeType إلى Skia، طوّر فريق Chrome برنامجًا جديدًا للتعامل مع الخطوط في Skia استنادًا إلى Skrifa وطرح التغيير تدريجيًا للمستخدمين:

بالنسبة إلى عملية الدمج في Chrome، نعتمد على الدمج السلس للغة Rust في قاعدة الرموز التي أضافها فريق أمان Chrome.

في المستقبل، سننتقل إلى استخدام Fontations أيضًا في خطوط نظام التشغيل، بدءًا من Linux وChromeOS، ثم على Android.

السلامة، أولاً وقبل كل شيء

هدفنا الأساسي هو الحدّ من الثغرات الأمنية (أو إزالتها تمامًا) الناتجة عن الوصول إلى الذاكرة خارج نطاقها. توفّر Rust هذه الميزة بدون الحاجة إلى أي إعدادات إضافية طالما أنّك تتجنّب أي مجموعات رموز برمجية غير آمنة.

تتطلّب أهداف الأداء التي نسعى إلى تحقيقها تنفيذ عملية واحدة غير آمنة حاليًا، وهي إعادة تفسير وحدات بايت عشوائية كبنية بيانات ذات نوع محدّد. يتيح لنا ذلك قراءة البيانات من ملف خط بدون إجراء نسخ غير ضرورية، وهو أمر ضروري لإنشاء محلّل سريع لملفات الخطوط.

ولتجنُّب استخدام رمز غير آمن، اخترنا تفويض هذه المسؤولية إلى bytemuck، وهي مكتبة Rust مصمَّمة خصيصًا لهذا الغرض، وقد تم اختبارها واستخدامها على نطاق واسع في جميع أنحاء النظام المتكامل. يضمن تركيز إعادة تفسير البيانات الأولية في bytemuck توفُّر هذه الوظيفة في مكان واحد ومراجعتها، كما يتيح تجنُّب تكرار الرمز غير الآمن لهذا الغرض. يهدف مشروع التحويل الآمن إلى دمج هذه الوظيفة مباشرةً في برنامج تجميع Rust، وسنبدأ في استخدامها فور توفّرها.

الدقة مهمة

تم إنشاء Skrifa من مكوّنات مستقلة، حيث تم تصميم معظم بنى البيانات لتكون غير قابلة للتغيير. ويساهم ذلك في تحسين قابلية القراءة والصيانة والتعددية. ويجعل الرمز البرمجي أكثر ملاءمة لاختبار الوحدات. وقد استفدنا من هذه الفرصة وأنتجنا مجموعة من 700 اختبار وحدة تقريبًا تغطي حزمة البرامج الكاملة، بدءًا من إجراءات التحليل ذات المستوى المنخفض وصولاً إلى الآلات الافتراضية ذات المستوى العالي.

تشير الصحة أيضًا إلى الدقة، ويُعدّ FreeType برنامجًا موثوقًا به جدًا في إنشاء مخططات تفصيلية عالية الجودة. ويجب أن تتطابق هذه الجودة مع جودة الصورة الأصلية لكي تكون بديلاً مناسبًا. ولتحقيق ذلك، أنشأنا أداة مخصّصة باسم fauntlet تقارن بين نتائج Skrifa وFreeType لمجموعات من ملفات الخطوط ضمن مجموعة كبيرة من الإعدادات. ويمنحنا ذلك بعض الضمانات بأنّنا سنتجنّب حدوث تراجع في الجودة.

بالإضافة إلى ذلك، قبل الدمج في Chromium، أجرينا مجموعة واسعة من عمليات مقارنة وحدات البكسل في Skia، حيث قارنّا عرض FreeType بعرض Skrifa وعرض Skia للتأكّد من أنّ اختلافات وحدات البكسل في أدنى مستوياتها، وذلك في جميع أوضاع العرض المطلوبة (في جميع أوضاع التنعيم والتلميح المختلفة).

الاختبار غير الواضح هو أداة مهمة لتحديد كيفية تفاعل برنامج مع إدخالات مشوّهة وضارة. لقد كنا نختبر رمزنا الجديد بشكل متواصل منذ يونيو 2024. ويشمل ذلك مكتبات Rust نفسها ورمز الدمج. على الرغم من أنّ أداة التشويش قد رصدت (حتى وقت كتابة هذا التقرير) 39 خطأ، من الجدير بالذكر أنّه لم يكن أيّ من هذه الأخطاء مهمًا من الناحية الأمنية. وقد تؤدي إلى نتائج مرئية غير مرغوب فيها أو حتى إلى أعطال يمكن التحكّم فيها، ولكنها لن تؤدي إلى ثغرات أمنية يمكن استغلالها.

هيا بنا!

نحن سعداء للغاية بنتائج جهودنا لاستخدام Rust في النص. إنّ توفير رمز برمجي أكثر أمانًا للمستخدمين وتحسين إنتاجية المطوّرين هو إنجاز كبير بالنسبة إلينا. ونخطّط لمواصلة البحث عن فرص لاستخدام Rust في حزم النصوص لدينا. إذا أردت معرفة المزيد، يمكنك الاطّلاع على Oxidize الذي يوضّح بعض خطط Google Fonts المستقبلية.