Skip to content
 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

html-sanitizer

Санитизация HTML для OneScript по принципу allowlist: в выводе остаются только явно разрешённые теги, атрибуты и протоколы URL — всё остальное удаляется или экранируется. Основное назначение — безопасный показ HTML, отрендеренного из Markdown недоверенных пользователей (например, README пакетов на хабе).

С версии 0.2.0 библиотека закрывает и обратную задачу — безопасную сборку собственного HTML из недоверенных значений: экранирование значений, валидация имён атрибутов, сборка атрибутов и ссылок с фильтрацией опасных схем URL (javascript:/data:/vbscript: и их обфускации).

Публичный интерфейс — один модуль-фасад СанитизацияHTML. Токенизатор, санитайзер, настройки и таблица сущностей лежат в src/internal и меняются без оглядки на обратную совместимость.

Безопасный = СанитизацияHTML.Очистить(НедоверенныйHTML);   // разбор чужого HTML
Настройки  = СанитизацияHTML.ПустыеНастройки();            // свой allowlist
Фрагмент   = СанитизацияHTML.Ссылка(Адрес, Текст);         // сборка своего HTML

Зависимостей времени выполнения нет — только стандартная библиотека OneScript.

Установка

opm install html-sanitizer

Быстрый старт

#Использовать html-sanitizer

БезопасныйHTML = СанитизацияHTML.Очистить(НедоверенныйHTML);

Профиль по умолчанию (НастройкиMarkdown) рассчитан на вывод типичного markdown-рендера, в том числе oscript-md.

Свой allowlist

Настройки = СанитизацияHTML.ПустыеНастройки()      // ни один тег не разрешён
    .РазрешитьТег("p")
    .РазрешитьТег("a", "href, title")
    .РазрешитьПротокол("ftp")
    .УстановитьRelВнешнихСсылок("nofollow noopener noreferrer");

БезопасныйHTML = СанитизацияHTML.Очистить(HTML, Настройки);

Профиль по умолчанию тоже можно дорабатывать:

Настройки = СанитизацияHTML.НастройкиMarkdown()
    .ЗапретитьТег("img")                   // тег удаляется, содержимое остаётся
    .УдалятьТегССодержимым("blockquote")   // тег удаляется вместе с содержимым
    .УстановитьRelВнешнихСсылок("");       // не добавлять rel внешним ссылкам

API

Метод Назначение
СанитизацияHTML.Очистить(HTML, Настройки = Неопределено) санитизация; без настроек — профиль markdown
СанитизацияHTML.НастройкиMarkdown() профиль по умолчанию (таблица ниже)
СанитизацияHTML.ПустыеНастройки() пустой allowlist для своего профиля
СанитизацияHTML.ЭкранироватьТекст(Текст) экранирование текста без разрешённых тегов
СанитизацияHTML.ЭкранироватьЗначение(Значение) то же для произвольного значения: Неопределено"", не-строки приводятся
СанитизацияHTML.ИмяАтрибутаДопустимо(Имя) валидация имени атрибута: [A-Za-z][A-Za-z0-9-]*, до 64 символов (имя не спасти экранированием — пробел и = проходят сквозь него)
СанитизацияHTML.Атрибут(Имя, Значение) фрагмент имя="значение" с экранированием значения; пустое значение или плохое имя — ""
СанитизацияHTML.БулевАтрибут(Имя, Условие) имя при Условие = Истина и допустимом имени, иначе ""
СанитизацияHTML.URLБезопасенДляHref(Адрес) allowlist схем http/https/mailto + относительные/якорные; обфускации (java	script:, JavaScript:, пробелы/управляющие) нейтрализуются нормализацией
СанитизацияHTML.Ссылка(Адрес, Текст, Класс = "", Внутренности = Неопределено) безопасная <a href>: адрес и текст экранируются, опасная схема — только текст (в <span> с классом)

Объект настроек, полученный от НастройкиMarkdown() или ПустыеНастройки():

Метод Назначение
.РазрешитьТег(Имя, Атрибуты = "") разрешить тег (атрибуты — строка через запятую или массив)
.ЗапретитьТег(Имя) убрать тег из allowlist (содержимое сохраняется)
.УдалятьТегССодержимым(Имя) удалять тег вместе с содержимым (как script)
.РазрешитьПротокол(Схема) / .ЗапретитьПротокол(Схема) протоколы URL для href/src
.УстановитьRelВнешнихСсылок(Значение) rel для внешних ссылок; "" — отключить

Все методы настроек возвращают сам объект — поддерживаются цепочки вызовов.

Allowlist профиля по умолчанию

Теги Разрешённые атрибуты
p, br, hr
h1h6
ul, ol, li
strong, em
code, pre class — только вида language-*
blockquote
a href, title (+ автоматический rel для внешних ссылок)
img src, alt, title
table, thead, tbody, tr
th, td align — только left / center / right
details, summary

Протоколы URL: http, https, mailto; относительные и протокол-относительные (//host/...) ссылки разрешены. Внешним ссылкам (http://, https://, //) добавляется rel="nofollow noopener" (настраивается).

Теги, удаляемые вместе с содержимым (по умолчанию): script, style, iframe, frame, frameset, object, embed, applet, form, button, select, option, textarea, svg, math, template, xmp, noembed, noframes, noscript, title, head, marquee. Прочие неразрешённые теги (div, span, …) удаляются с сохранением текстового содержимого.

Гарантии

  • В выводе — только разрешённые теги и разрешённые для них атрибуты; весь текст и значения атрибутов экранированы.
  • URL в href/src проходят проверку протокола после однократного раскодирования HTML-сущностей (&#106;avascript:, &colon;, &Tab; и т.п.) и удаления управляющих/невидимых символов (таб, перевод строки, &#14;, zero-width, BOM, RTLO/LRO); javascript:, vbscript:, data: и любые схемы вне allowlist приводят к удалению атрибута, включая обфускации смешанным регистром.
  • Комментарии (включая conditional comments), CDATA, доктайпы, инструкции обработки удаляются целиком; нулевые байты вырезаются до разбора.
  • Малформед-HTML не ломает вывод: незакрытые разрешённые теги закрываются, перепутанный порядок закрытия исправляется, лишние закрывающие теги отбрасываются, незакрытая кавычка атрибута не «открывает» новые теги, < вне тега экранируется в &lt;.
  • Дублирующиеся атрибуты: действует первое вхождение (как в браузере).
  • Раскодирование сущностей выполняется ровно один раз — двойное кодирование (&amp;#106;…) не даёт исполнимую схему.
  • Fail-closed: незакрытый опасный тег (<script> без закрытия) удаляет всё до конца документа; сомнительная схема URL удаляет атрибут.

Ограничения (что НЕ ловится)

  • Гомоглифы в доменах (https://еxample.com с кириллической «е») — это валидные URL, они проходят. Защита от фишинга доменов — задача уровня отображения/линтера ссылок, не санитайзера.
  • RTLO и bidi-символы в обычном тексте сохраняются (в URL — удаляются): влияние на порядок отображения текста не является XSS.
  • data:-изображения удаляются вместе с src — инлайн-картинки из markdown потребуют отдельного решения, сознательный компромисс.
  • Санитайзер не валидирует семантическую вложенность HTML (например, li вне ul) — только безопасность и балансировку тегов.
  • Неизвестные именованные сущности остаются буквальным текстом (&amp;foo;), числовые сущности за пределами BMP (> &#65535;) не раскодируются и остаются экранированным текстом.

Архитектура

Наружу выставлен только фасад; всё, что ниже, — деталь реализации:

src/Модули/СанитизацияHTML.os      публичный фасад (#Использовать "../internal")
src/internal/Классы/               ТокенизаторHTML, СанитайзерHTML, НастройкиСанитизации
src/internal/Модули/               СущностиHTML
  • ТокенизаторHTML — однопроходный разбор HTML на токены (текст, открывающий и закрывающий теги) с повторением ключевых решений HTML5-парсера для малформед-входа; комментарии/CDATA/декларации пропускаются на этом уровне.
  • СанитайзерHTML — фильтрация потока токенов по настройкам, проверка URL, балансировка тегов стеком, сериализация безопасного вывода.
  • НастройкиСанитизации — allowlist тегов/атрибутов, протоколы, политика rel.
  • СущностиHTML — однократное раскодирование и экранирование сущностей.

Тесты

oneunit execute -d tests

Корпус: 93 теста, включая 70+ XSS-векторов по мотивам OWASP XSS Filter Evasion Cheat Sheet, малформед-корпус, юникод и негативные сценарии настроек. Тестам внутренностей доступен #Использовать "../src/internal".

Лицензия

MIT

About

Санитизация HTML по принципу allowlist для OneScript: фильтрация тегов, атрибутов и протоколов URL, безопасная сборка собственного HTML

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages