
Ruby HTML and CSS sanitizer.
Sanitize هو أداة تعقيم HTML وCSS تعتمد على قائمة السماح (allowlist). يزيل كل HTML و/أو CSS من سلسلة نصية باستثناء العناصر والسمات والخصائص التي تختار السماح بها.
باستخدام صيغة تكوين بسيطة، يمكنك إخبار Sanitize بالسماح بعناصر HTML معينة، وسمات معينة داخل تلك العناصر، وحتى بروتوكولات URL معينة داخل السمات التي تحتوي على عناوين URL. يمكنك أيضًا السماح بخصائص CSS محددة، وقواعد @، وبروتوكولات URL في العناصر أو السمات التي تحتوي على CSS. سيتم إزالة أي HTML أو CSS لا تسمح به صراحةً.
يعتمد Sanitize على محلل Nokogiri لـ HTML5، الذي يحلل HTML بنفس طريقة المتصفحات الحديثة، وعلى Crass، الذي يحلل CSS بنفس طريقة المتصفحات الحديثة. طالما أن إعداد قائمة السماح لديك يسمح فقط بترميز وCSS آمنين، فحتى أكثر المدخلات تشوهًا أو خبثًا ستتحول إلى مخرجات آمنة.
gem install sanitize
## البدء السريع```ruby
require 'sanitize'
# Clean up an HTML fragment using Sanitize's permissive but safe Relaxed config.
# This also sanitizes any CSS in `<style>` elements or `style` attributes.
Sanitize.fragment(html, Sanitize::Config::RELAXED)
# Clean up an HTML document using the Relaxed config.
Sanitize.document(html, Sanitize::Config::RELAXED)
# Clean up a standalone CSS stylesheet using the Relaxed config.
Sanitize::CSS.stylesheet(css, Sanitize::Config::RELAXED)
# Clean up some CSS properties using the Relaxed config.
Sanitize::CSS.properties(css, Sanitize::Config::RELAXED)
يمكن لـ Sanitize تنظيف الأنواع التالية من الإدخال:
<style> في HTMLstyle في HTML[!WARNING]
لا يمكن لـ Sanitize تنظيف محتويات عناصر
<math>أو<svg>بشكل كامل. عناصر MathML وSVG هي عناصر أجنبية لا تتبع قواعد تحليل HTML العادية.افتراضيًا، سيزيل Sanitize جميع عناصر MathML وSVG. إذا أضفت عناصر MathML أو SVG إلى قائمة العناصر المخصصة المسموح بها، فقد تنشئ ثغرة أمنية في تطبيقك.
الجزء هو مقتطف من HTML لا يحتوي على عنصر <html> على مستوى الجذر.
إذا لم تحدد أي خيارات تكوين، فسيستخدم Sanitize إعداداته الأكثر صرامة افتراضيًا، مما يعني أنه سيزيل كل HTML ويترك النص الآمن فقط.```ruby
html = 'foo
'
Sanitize.fragment(html)
للحفاظ على عناصر معينة، أضفها إلى قائمة السماح بالعناصر.```ruby
Sanitize.fragment(html, elements: ['b'])
# => "<b>foo</b>"
عند تعقيم مستند، يجب أن يكون عنصر <html> مدرجًا في قائمة السماح. يمكنك أيضًا ضبط :allow_doctype على true للسماح بتعريفات نوع المستند جيدة الصياغة.```ruby
html = %[
]
Sanitize.document(html, allow_doctype: true, elements: ['html'] )
### CSS في HTML
لتعقيم CSS في جزء أو مستند HTML، قم أولاً بإدراج عنصر `<style>` و/أو سمة `style` في القائمة البيضاء. ثم أدرج خصائص CSS وقواعد @ وبروتوكولات URL التي تريد السماح بها. يمكنك أيضاً اختيار ما إذا كنت تريد السماح بتعليقات CSS أو حيل التوافق مع المتصفحات.```ruby
html = %[
<style>
div { color: green; width: 1024px; }
</style>
<div style="height: 100px; width: 100px;"></div>
<p>hello!</p>
]
Sanitize.fragment(html,
elements: ['div', 'style'],
attributes: {'div' => ['style']},
css: {
properties: ['width']
}
)
#=> %[
# <style>
# div { width: 1024px; }
# </style>
#
# <div style=" width: 100px;"></div>
# hello!
# ]
يمكن لـ Sanitize تنظيف ورقة أنماط CSS مستقلة أو سلسلة خصائص بسهولة دون الحاجة إلى استدعاء محلل HTML.
[!WARNING] يجب استخدام هذا فقط لتنظيف CSS لاستخدامه في ملف CSS مستقل. لتنظيف CSS بأمان لاستخدامه في سياق HTML، مثل عنصر
<style>أو سمةstyle، يجب استخدامSanitize.document()أوSanitize.fragment()مع قائمة سماح مناسبة كما هو موضح في الأقسام السابقة. عدم القيام بذلك قد يتيح هجمات حقن HTML.```ruby css = %[ @import url(evil.css);
a { text-decoration: none; }
a:hover { left: expression(alert('xss!')); text-decoration: underline; } ]
Sanitize::CSS.stylesheet(css, Sanitize::Config::RELAXED)
Sanitize::CSS.properties(%[ left: expression(alert('xss!')); text-decoration: underline; ], Sanitize::Config::RELAXED)
## الإعدادات
بالإضافة إلى الإعدادات الافتراضية فائقة الأمان، تأتي Sanitize مع ثلاثة إعدادات مدمجة أخرى يمكنك استخدامها مباشرة أو تكييفها لتناسب احتياجاتك.
### Sanitize::Config::RESTRICTED
يسمح فقط بترميز نصي بسيط جدًا. لا روابط، ولا صور، ولا عناصر كتلة.```ruby
Sanitize.fragment(html, Sanitize::Config::RESTRICTED)
# => "<b>foo</b>"
يسمح بمجموعة متنوعة من عناصر الترميز بما في ذلك عناصر التنسيق والروابط والقوائم.
لا يُسمح بالصور والجداول، وتقتصر الروابط على بروتوكولات FTP وHTTP وHTTPS وmailto، وتُضاف سمة rel="nofollow" إلى جميع الروابط للتخفيف من البريد العشوائي لتحسين محركات البحث (SEO).```ruby
Sanitize.fragment(html, Sanitize::Config::BASIC)
### Sanitize::Config::RELAXED
يسمح بنطاق أوسع من الترميزات، بما في ذلك الصور والجداول، بالإضافة إلى CSS آمن. لا تزال الروابط مقتصرة على بروتوكولات FTP وHTTP وHTTPS وmailto، بينما تقتصر الصور على HTTP وHTTPS. في هذا الوضع، لا تتم إضافة `rel="nofollow"` إلى الروابط.```ruby
Sanitize.fragment(html, Sanitize::Config::RELAXED)
# => '<b><a href="http://foo.com/">foo</a></b><img src="https://raw.githubusercontent.com/rgrove/sanitize/HEAD/bar.jpg">'
إذا كانت الأوضاع المدمجة لا تلبي احتياجاتك، يمكنك بسهولة تحديد تكوين مخصص:```ruby Sanitize.fragment(html, elements: ['a', 'span'],
attributes: { 'a' => ['href', 'title'], 'span' => ['class'] },
protocols: { 'a' => {'href' => ['http', 'https', 'mailto']} } )
يمكنك أيضًا البدء بأحد الإعدادات المدمجة في Sanitize ثم تخصيصه ليلائم احتياجاتك.
الإعدادات المدمجة مجمّدة بعمق لمنع الأشخاص من تعديلها (سواء عن طريق الخطأ أو بقصد خبيث). لتخصيص إعداد مدمج، أنشئ نسخة جديدة باستخدام `Sanitize::Config.merge()`، على النحو التالي:```ruby
# Create a customized copy of the Basic config, adding <div> and <table> to the
# existing allowlisted elements.
Sanitize.fragment(html, Sanitize::Config.merge(Sanitize::Config::BASIC,
elements: Sanitize::Config::BASIC[:elements] + ['div', 'table'],
remove_contents: true
))
المثال أعلاه يضيف العنصرين <div> و <table> إلى نسخة من قائمة العناصر الموجودة في Sanitize::Config::BASIC. إذا أردت بدلاً من ذلك الكتابة فوق مصفوفة العناصر بالكامل بمصفوفتك الخاصة، يمكنك حذف العملية +:```ruby
Sanitize.fragment(html, Sanitize::Config.merge(Sanitize::Config::BASIC, elements: ['div', 'table'], remove_contents: true ))
### إعدادات التكوين
#### :add_attributes (Hash)
سمات لإضافتها إلى عناصر محددة. إذا كانت السمة موجودة بالفعل، فسيتم استبدالها بالقيمة المحددة هنا. حدد جميع أسماء العناصر والسمات بأحرف صغيرة.```ruby
add_attributes: {
'a' => {'rel' => 'nofollow'}
}
ما إذا كان سيتم السماح بتعليقات HTML أم لا. يُثبط بشدة السماح بالتعليقات، نظرًا لأن IE يسمح بتنفيذ البرامج النصية داخل التعليقات الشرطية. القيمة الافتراضية هي false.
ما إذا كان سيتم السماح بإعلانات doctype الخاصة بـ HTML جيدة التنسيق مثل "" عند تعقيم مستند أم لا. يتم تجاهل هذا الإعداد عند تعقيم الأجزاء. القيمة الافتراضية هي false.
السمات التي يُسمح بها على عناصر محددة. حدد جميع أسماء العناصر والسمات بأحرف صغيرة.```ruby attributes: { 'a' => ['href', 'title'], 'blockquote' => ['cite'], 'img' => ['alt', 'src', 'title'] }
إذا كنت ترغب في السماح بسمات معينة على جميع العناصر، استخدم الرمز `:all` بدلاً من اسم العنصر.```ruby
# Allow the class attribute on all elements.
attributes: {
:all => ['class'],
'a' => ['href', 'title']
}
للسماح بسمات HTML5 data-* العشوائية، استخدم الرمز :data بدلاً من اسم السمة.```ruby
attributes: { 'div' => [:data] }
#### :css (Hash)
هاش (Hash) لإعدادات تهيئة CSS التالية لاستخدامها عند تعقيم CSS (سواء كان مستقلاً أو مضمّنًا في HTML).
##### :css => :allow_comments (boolean)
ما إذا كان سيتم السماح بتعليقات CSS أم لا. القيمة الافتراضية هي `false`.
##### :css => :allow_hacks (boolean)
ما إذا كان سيتم السماح باختراقات توافق المتصفح مثل اختراقات IE `*` و `_`. هذه عادةً ما تكون غير ضارة، ولكنها تقنيًا تؤدي إلى CSS غير صالح. القيمة الافتراضية هي `false`.
##### :css => :at_rules (Array or Set)
أسماء [at-rules][at-rules] الخاصة بـ CSS التي يُسمح بها والتي قد لا تحتوي على كتل مرتبطة، مثل `import` أو `charset`. يجب تحديد الأسماء بأحرف صغيرة.
[at-rules]:https://developer.mozilla.org/en-US/docs/Web/CSS/At-rule
##### :css => :at_rules_with_properties (Array or Set)
أسماء [at-rules][at-rules] الخاصة بـ CSS التي يُسمح بها والتي قد تحتوي على كتل مرتبطة تحتوي على خصائص CSS. قواعد at-rules مثل `font-face` و `page` تندرج ضمن هذه الفئة. يجب تحديد الأسماء بأحرف صغيرة.
##### :css => :at_rules_with_styles (Array or Set)
أسماء [at-rules][at-rules] الخاصة بـ CSS التي يُسمح بها والتي قد تحتوي على كتل مرتبطة تحتوي على قواعد أنماط. قواعد at-rules مثل `media` و `keyframes` تندرج ضمن هذه الفئة. يجب تحديد الأسماء بأحرف صغيرة.
##### :css => :import_url_validator
هذا `Proc` (أو كائن قابل للاستدعاء) سيتم استدعاؤه وتمرير عنوان URL المحدد لأي [at-rules][at-rules] من نوع `@import`.
يمكنك استخدام هذا لتقييد ما يمكن استيراده، على سبيل المثال شيء مثل التالي لتقييد `@import` بعناوين Google Fonts:```ruby
Proc.new { |url| url.start_with?("https://fonts.googleapis.com") }
قائمة بأسماء خصائص CSS المسموح بها. يجب تحديد الأسماء بأحرف صغيرة.
بروتوكولات URL المسموح بها في روابط CSS. يجب تحديدها بأحرف صغيرة.
إذا كنت ترغب في السماح باستخدام الروابط النسبية التي لا تحتوي على بروتوكول، فقم بتضمين الرمز :relative في مصفوفة البروتوكولات.
مصفوفة بأسماء عناصر HTML المسموح بها. حدد جميع الأسماء بأحرف صغيرة. سيتم إزالة أي عناصر غير موجودة في هذه المصفوفة.```ruby elements: %w[ a abbr b blockquote br cite code dd dfn dl dt em i kbd li mark ol p pre q s samp small strike strong sub sup time u ul var ]
> [!WARNING]
>
> لا يمكن لـ Sanitize تنقية محتويات عناصر `<math>` أو `<svg>` بشكل كامل. تعد عناصر MathML وSVG [عناصر أجنبية](https://html.spec.whatwg.org/multipage/syntax.html#foreign-elements) لا تتبع قواعد تحليل HTML العادية.
>
> بشكل افتراضي، يزيل Sanitize جميع عناصر MathML وSVG. إذا أضفت عناصر MathML أو SVG إلى قائمة السماح المخصصة للعناصر، فيجب عليك افتراض أن أي محتوى بداخلها سيُسمح به، حتى لو كان هذا المحتوى سيُزال أو يُرمَّز بواسطة Sanitize. قد يؤدي هذا إلى إنشاء ثغرة أمنية في تطبيقك.
> [!NOTE]
>
> يزيل Sanitize دائمًا عناصر `<noscript>` ومحتوياتها، حتى إذا كان `noscript` في قائمة السماح.
>
> وذلك لأن محتوى عنصر `<noscript>` يُحلَّل بشكل مختلف في المتصفحات اعتمادًا على ما إذا كانت البرمجة النصية مفعّلة أم لا. نظرًا لأن Nokogiri لا يدعم البرمجة النصية، فهو يحلل دائمًا عناصر `<noscript>` كما لو كانت البرمجة النصية معطّلة. وينتج عن هذا حالات حافة حيث لا يمكن تنقية محتويات عنصر `<noscript>` بشكل موثوق لأن Nokogiri لا يمكنه تكرار سلوك التحليل في متصفح مفعّل للبرمجة النصية بشكل كامل.
#### :parser_options (Hash)
[خيارات التحليل](https://nokogiri.org/tutorials/parsing_an_html5_document.html?h=parsing+options#parsing-options) التي سيتم توفيرها إلى Nokogiri.```ruby
parser_options: {
max_errors: -1,
max_tree_depth: -1
}
بروتوكولات URL المسموح بها في سمات محددة. إذا تم إدراج سمة هنا واحتوت على بروتوكول غير تلك المحددة (أو إذا لم تحتوِ على أي بروتوكول على الإطلاق)، سيتم إزالتها.```ruby protocols: { 'a' => {'href' => ['ftp', 'http', 'https', 'mailto']}, 'img' => {'src' => ['http', 'https']} }
إذا كنت ترغب في السماح باستخدام عناوين URL النسبية التي لا تحتوي على بروتوكول، فقم بتضمين الرمز `:relative` في مصفوفة البروتوكول:```ruby
protocols: {
'a' => {'href' => ['http', 'https', :relative]}
}
إذا كانت هذه القيمة true، فسيزيل Sanitize محتويات أي عناصر غير مدرجة في قائمة السماح بالإضافة إلى العناصر نفسها. افتراضيًا، يُبقي Sanitize على الأجزاء الآمنة من محتويات العنصر عند إزالة العنصر.
إذا كانت هذه القيمة عبارة عن Array أو Set من أسماء العناصر، فسيتم إزالة محتويات العناصر المحددة فقط (عند تصفيتها)، بينما سيتم الإبقاء على محتويات جميع العناصر المصفاة الأخرى.
يمكن الاطلاع على القيمة الافتراضية في الإعداد الافتراضي.
محوّل HTML مخصص أو مصفوفة من المحولات المخصصة. راجع قسم Transformers أدناه للحصول على التفاصيل.
Hash من أسماء العناصر التي، عند إزالتها، يجب أن تُحاط محتوياتها بمسافات بيضاء للحفاظ على قابلية القراءة.
كل اسم عنصر هو مفتاح يشير إلى Hash آخر، يوفّر المسافات البيضاء المحددة التي يجب إدراجها قبل موضع العنصر المزال وبعده (:before و:after). سيتم إدراج قيمة :after فقط إذا كان للعنصر المزال عناصر فرعية، وفي هذه الحالة سيتم إدراجها بعد تلك العناصر الفرعية.```ruby
whitespace_elements: {
'br' => { before: "\n", after: "" },
'div' => { before: "\n", after: "\n" },
'p' => { before: "\n", after: "\n" }
}
يمكن رؤية العناصر الافتراضية مع إضافة مسافات بيضاء قبلها وبعدها في [التهيئة الافتراضية](https://github.com/rgrove/sanitize/blob/HEAD/lib/sanitize/config/default.rb).
## المحوّلات
تسمح لك المحوّلات بتصفية وتعديل عُقد HTML باستخدام منطقك المخصص، بالإضافة إلى (أو بدلاً من) عامل التصفية الأساسي في Sanitize. المحوّل هو أي كائن يستجيب إلى `call()` (مثل lambda أو proc).
لاستخدام محوّل واحد أو أكثر، مرّرها إلى إعداد التهيئة `:transformers`. يمكنك تمرير محوّل واحد أو مصفوفة من المحوّلات.```ruby
Sanitize.fragment(html, transformers: [
transformer_one,
transformer_two
])
سيتم استدعاء طريقة call() لكل محوّل مرة واحدة لكل عقدة في HTML (بما في ذلك العناصر، وعُقد النصوص، والتعليقات، وغيرها)، وسيتلقى كوسيطة Hash يحتوي على العناصر التالية:
:config - إعدادات Sanitize الحالية على شكل Hash.
:is_allowlisted - true إذا كانت العقدة الحالية قد أُدرجت في القائمة البيضاء بواسطة محوّل سابق، وfalse خلاف ذلك. من السيئ عمومًا إزالة عقدة أدرجها محوّل سابق في القائمة البيضاء.
:node - كائن Nokogiri::XML::Node يمثل عقدة HTML. قد تكون العقدة عنصرًا، أو عقدة نصية، أو تعليقًا، أو عقدة CDATA، أو جزء مستند. استخدم طرق الفحص في Nokogiri (element?, text?, إلخ) لتجاهل أنواع العقد التي لا تهمك بشكل انتقائي.
:node_allowlist - مجموعة من كائنات Nokogiri::XML::Node في المستند الحالي التي أُدرجت في القائمة البيضاء بواسطة محوّلات سابقة، إن وجدت. من السيئ عمومًا إزالة عقدة أدرجها محوّل سابق في القائمة البيضاء.
:node_name - اسم عقدة HTML الحالية، دائمًا بأحرف صغيرة (مثل "div" أو "span"). بالنسبة للعقد غير العنصرية، سيكون الاسم شيئًا مثل "text" أو "comment" أو "#cdata-section" أو "#document-fragment" إلخ.
لا يتعين على المحوّل إرجاع أي شيء، لكن يمكنه اختياريًا إرجاع Hash قد يحتوي على العناصر التالية:
Nokogiri::XML::Node المحددة لإضافتها إلى القائمة البيضاء للمستند، متجاوزًا إعدادات Sanitize الحالية. ستُدرج هذه العقد المحددة وجميع سماتها في القائمة البيضاء، لكن لن تُدرج أبناؤها.إذا أرجَع المحوّل أي شيء آخر غير Hash، فسيتم تجاهل القيمة المُرجعة.
يتمتع كل محوّل بوصول كامل إلى Nokogiri::XML::Node الذي يُمرَّر إليه وإلى بقية المستند عبر طريقة document() الخاصة بالعقدة. ستنعكس أي تغييرات تُجرى على العقدة الحالية أو على المستند فورًا في المستند وستُمرَّر إلى المحولات التي تُستدعى لاحقًا وإلى Sanitize نفسه. بل يمكن للمحوّل حتى استدعاء Sanitize داخليًا لإجراء تنظيف مخصص إذا لزم الأمر.
تُمرَّر العقد إلى المحولات بالترتيب الذي يتم فيه اجتيازها. يقوم Sanitize باجتياز من الأعلى إلى الأسفل، مما يعني أن العقد تُجتاز بنفس الترتيب الذي ستقرؤها به في HTML، بدءًا من العقدة العلوية، ثم أول طفل لها، وهكذا.```ruby html = %[
foobar
]transformer = lambda do |env| puts env[:node_name] if env[:node].element? end
Sanitize.fragment(html, transformers: transformer)
تمتلك Transformers قدرًا هائلًا من القوة، بما في ذلك القدرة على تجاوز التصفية المدمجة في Sanitize تمامًا. كن حذرًا! سلامتك بين يديك.
### مثال: Transformer للسماح بعناوين URL الخاصة بالصور حسب النطاق
يوضح المثال التالي كيفية إزالة عناصر الصور ما لم تستخدم عنوان URL نسبيًا أو تكون مستضافة على نطاق محدد. ويفترض أن عنصر `` وسمة `src` الخاصة به مدرجان بالفعل في القائمة البيضاء.```ruby
require "uri"
image_allowlist_transformer = lambda do |env|
# Ignore everything except elements.
return unless env[:node_name] == "img"
node = env[:node]
image_uri = URI.parse(node["src"])
# Only allow relative URLs or URLs with the example.com domain. The
# image_uri.host.nil? check ensures that protocol-relative URLs like
# "//evil.com/foo.jpg" are not allowed.
unless image_uri.host == "example.com"
unless image_uri.host.nil? && image_uri.relative?
node.unlink # `Nokogiri::XML::Node#unlink` removes a node from the document
end
end
end
يوضح المثال التالي كيفية إنشاء محوّل (Transformer) يسمح بأمان بتضمين فيديوهات YouTube الصالحة دون الحاجة إلى السماح بأنواع أخرى من المحتوى المضمّن، وهو ما قد يحدث إذا حاولت فعل ذلك بمجرد السماح بجميع عناصر ` ].strip
Sanitize.fragment(html, transformers: youtube_transformer)