HTML Sanitizer

Дата обновления перевода 2024-07-30

HTML Sanitizer

Компонент HTML Sanitizer предназначен для проверки/очистки ненадежного HTML кода (например, созданного WYSIWYG-редактором в браузере) в HTML, которому можно доверять. Он основан на HTML Sanitizer W3C Standard Proposal.

Дезинфектор HTML создает новую структуру HTML с нуля, принимая только
элементы и атрибуты, которые разрешены конфигурацией. Это означает.

что возвращаемый HTML очень предсказуем (он содержит только разрешенные элементы), но он не очень хорошо работает с плохо отформатированным вводом (например. невалидным HTML). Дезинфектор предназначен для двух случаев использования:

  • Предотвращение атак безопасности, основанных на XSS или других технологиях, полагающихся на выполнение вредоносного кода в браузере посетителя;
  • Генерация HTML, который всегда соблюдает определенный формат (только определенные теги, атрибуты, хосты и т.д.), чтобы иметь возможность последовательно стилизовать полученный результат с помощью CSS. Это также защищает ваше приложение от атак, связанных, например, с изменением CSS всей страницы.

Установка

Вы можете установить компонент HTML Sanitizer с помощью:

1
$ composer require symfony/html-sanitizer

Базовое использование

Используйте класс HtmlSanitizer для для дезинфекции HTML. Во фреймворке Symfony этот класс доступен как сервис html_sanitizer. Этот сервис будет автосмонтирован` при использовании подсказки типов для HtmlSanitizerInterface:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// src/Controller/BlogPostController.php
namespace App\Controller;

// ...
use Symfony\Component\HtmlSanitizer\HtmlSanitizerInterface;

class BlogPostController extends AbstractController
{
    public function createAction(HtmlSanitizerInterface $htmlSanitizer, Request $request): Response
    {
        $unsafeContents = $request->getPayload()->get('post_contents');

        $safeContents = $htmlSanitizer->sanitize($unsafeContents);
        // ... продолжать использовать безопасный HTML
    }
}

Note

Конфигурация по умолчанию дезинфектора HTML позволяет использовать все "безопасные" элементы и атрибуты, как определено в W3C Standard Proposal. На практике это означает, что полученный код не будет содержать никаких скриптов, стилей или других элементов, которые могут заставить сайт вести себя или выглядеть по-другому. Позже в этой статье вы узнаете, как полностью настраивать HTML-дезинфектор .

Дезинфекция HTML для конкретного контекста

Метод по умолчанию sanitize() очищает HTML-код для использования в элементе <body>. Используя метод sanitizeFor() вы можете указать дезинфектору HTML настраивать это для <head> или более конкретного HTML-тега:

1
2
3
4
5
6
7
8
9
10
// теги, не разрешенные в <head>, будут удалены
$safeInput = $htmlSanitizer->sanitizeFor('head', $userInput);

// шифрует возвращенный HTML с использованием сущностей HTML
$safeInput = $htmlSanitizer->sanitizeFor('title', $userInput);
$safeInput = $htmlSanitizer->sanitizeFor('textarea', $userInput);

// использует контекст <body>, удаляя только теги, разрешенные в <head>
$safeInput = $htmlSanitizer->sanitizeFor('body', $userInput);
$safeInput = $htmlSanitizer->sanitizeFor('section', $userInput);

Дезинфекция HTML от ввода формы

Компонент HTML Sanitizer напрямую интегрируется с Symfony Forms,
для дезинфекции ввода формы перед его обработкой вашим приложением.

Вы можете включить дезинфектор в формах TextType или любых формах, расширяющих этот тип (например, TextareaType), используя опцию sanitize_html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// src/Form/BlogPostType.php
namespace App\Form;

// ...
class BlogPostType extends AbstractType
{
    // ...

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'sanitize_html' => true,
            // использовать опцию "sanitizer", чтобы использовать пользовательский дезинфектор (см. ниже)
            //'sanitizer' => 'app.post_sanitizer',
        ]);
    }
}

Дезинфекция HTML в шаблонах Twig

Помимо дезинфекции пользовательского ввода, вы также можете дезинфицировать HTML-код перед
его выводом в шаблоне Twig, с помощью фильтра sanitize_html():

1
2
3
4
{{ post.body|sanitize_html }}

{# вы также можете использовать пользовательский дезинфектор (см. ниже) #}
{{ post.body|sanitize_html('app.post_sanitizer') }}

Конфигурация

Поведение HTML-дезинфектора может быть полностью настроено. Это позволяет вам явно указать, какие элементы, атрибуты и даже значения атрибутов разрешены.

Это можно сделать, определив новый HTML-дезинфектор в конфигурации:

1
2
3
4
5
6
7
# config/packages/html_sanitizer.yaml
framework:
    html_sanitizer:
        sanitizers:
            app.post_sanitizer:
                block_elements:
                    - h1

Эта конфигурация определяет новый сервис html_sanitizer.sanitizer.app.post_sanitizer. Этот сервис будет `.

Разрешить базовые значения элементов

Вы можете запустить пользовательский дезинфектор HTML, используя одну из двух базовых версий:

Статические элементы
Все элементы и атрибуты на базовой линии допускают списки из W3C Standard Proposal (сюда не входят скрипты).
Безопасные элементы
Все элементы и атрибуты из списка "статических элементов", исключая элементы и атрибуты, которые также могут привести к CSS внедрению/перехвату кликов.
1
2
3
4
5
6
7
8
# config/packages/html_sanitizer.yaml
framework:
    html_sanitizer:
        sanitizers:
            app.post_sanitizer:
                # включить одно из этого
                allow_safe_elements: true
                allow_static_elements: true

Разрешить элементы

Это добавляет элементы в список разрешенных. Для каждого элемента можно также указать разрешенные атрибуты этого элемента. Если они не указаны, то разрешены все атрибуты из W3C Standard Proposal.

1
2
3
4
5
6
7
8
9
10
11
12
13
# config/packages/html_sanitizer.yaml
framework:
    html_sanitizer:
        sanitizers:
            app.post_sanitizer:
                # ...
                allow_elements:
                    # разрешить элемент <article> и 2 атрибута
                    article: ['class', 'data-attr']
                    # разрешить элемент <img> и сохранить атрибут src
                    img: 'src'
                    # разрешить элемент <h1> со всеми безопасными атрибутами
                    h1: '*'

Блокировать и скидывать элементы

Вы также можете блокировать (элемент будет удален, но его дочерние элементы будут сохранены) или сбрасывать (элемент и его дочерние элементы будут удалены).

Это также можно использовать для удаления элементов из списка разрешенных.

1
2
3
4
5
6
7
8
9
10
11
# config/packages/html_sanitizer.yaml
framework:
    html_sanitizer:
        sanitizers:
            app.post_sanitizer:
                # ...

                # удалить <div>, но обработать дочерние
                block_elements: ['div']
                # удалить <figure> и его дочерние элементы
                drop_elements: ['figure']

Разрешить атрибуты

С помощью этой опции вы можете указать, какие атрибуты будут сохранены в возвращаемом HTML. Атрибут будет разрешен для заданных элементов или для всех элементов, разрешенных до этой настройки.

1
2
3
4
5
6
7
8
9
10
11
12
# config/packages/html_sanitizer.yaml
framework:
    html_sanitizer:
        sanitizers:
            app.post_sanitizer:
                # ...
                allow_attributes:
                    # разрешить "src' в элементах <iframe>
                    src: ['iframe']

                    # разрешить "data-attr" во всех элементах, разрешенных на данный момент
                    data-attr: '*'

Сбросить атрибуты

Эта опция позволяет запретить атрибуты, которые были разрешены ранее.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# config/packages/html_sanitizer.yaml
framework:
    html_sanitizer:
        sanitizers:
            app.post_sanitizer:
                # ...
                allow_attributes:
                    # разрешить "data-attr" во всех безопасных элементах...
                    data-attr: '*'

                drop_attributes:
                    # ...кроме элемента <section>
                    data-attr: ['section']
                    # запрещает "style' в любом разрешенном элементе
                    style: '*'

Форсировать значения атрибутов

С помощью этой опции вы можете принудительно установить атрибут с заданным значением в элементе. Например, используйте следующую конфигурацию, чтобы всегда устанавливать
rel=«noopener noreferrer» в каждом <a> элементе (даже если исходный элемент не содержал атрибута rel):

1
2
3
4
5
6
7
8
9
# config/packages/html_sanitizer.yaml
framework:
    html_sanitizer:
        sanitizers:
            app.post_sanitizer:
                # ...
                force_attributes:
                    a:
                        rel: noopener noreferrer

Форсировать/разрешить ссылки URL

Помимо разрешения/блокировки элементов и атрибутов, вы также можете управлять URL- адресами элементов <a>:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# config/packages/html_sanitizer.yaml
framework:
    html_sanitizer:
        sanitizers:
            app.post_sanitizer:
                # ...

                # если `true`, все URL, использующие схему `http://`, будут преобразованы для
                # использования схемы `https://` вместо этого. `http` все еще должен быть разрешен
                # в `allowed_link_schemes`
                force_https_urls: true

                # указывает разрешенные схемы URL. Если URL имеет другую схему, атрибут
                # будет отброшен
                allowed_link_schemes: ['http', 'https', 'mailto']

                # указывает разрешенные хосты, атрибут будет отброшен, если URL содержит
                # другой хост. Субдомены разрешены: например, следующая конфигурация
                # будет также позволять 'www.symfony.com', 'live.symfony.com', и т.д.
                allowed_link_hosts: ['symfony.com']

                # разрешать ли относительные ссылки (т.е. URL без схемы и хоста)
                allow_relative_links: true

Форсировать/разрешить медиа URL

Подобно ссылкам на URL , вы также можете управлять URL других носителей в HTML. Следующие атрибуты проверяются дезинфектором HTML: rc, href, lowsrc, background и ``ping''.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# config/packages/html_sanitizer.yaml
framework:
    html_sanitizer:
        sanitizers:
            app.post_sanitizer:
                # ...

                # если `true`, все URL, использующие схему `http://`, будут преобразованы, чтобы
                # использовать схему `https://` вместо этого. `http` все еще должен быть разрешен
                # в `allowed_media_schemes`
                force_https_urls: true

                # указывает разрешенные схемы URL. Если URL имеет другую схему, атрибут
                # будет отброшен
                allowed_media_schemes: ['http', 'https', 'mailto']

                # указывает разрешенные хосты, атрибут будет отброшен, если URL содержит
                # другой хост, который не является субдоменом разрешенного хоста
                allowed_media_hosts: ['symfony.com'] # Также разрешает любой субдомен (т.е. www.symfony.com)

                # разрешать ли относительные URL (т.е. URL без схемы и хоста)
                allow_relative_medias: true

Максимальная длина ввода

Для предотвращения DoS-атак, по умолчанию дезинфектор HTML ограничивает длину текста ввода до 20000 символов (измеряется strlen($input)). Все содержание, превышающее эту длину, будет усечено. Используйте эту опцию, чтобы увеличить или уменьшить это ограничение:

1
2
3
4
5
6
7
8
9
# config/packages/html_sanitizer.yaml
framework:
    html_sanitizer:
        sanitizers:
            app.post_sanitizer:
                # ...

                # ввод длиннее (в символах), чем это значение, будет обрезан
                max_input_length: 30000 # default: 20000

Можно отключить это ограничение длины, установив максимальную длину ввода в значение -1. Остерегайтесь, что это может подвергнуть ваше приложение DoS-атакам.

Пользовательские дезинфекторы атрибутов

Управление ссылками и медиа-URL осуществляется с помощью UrlAttributeSanitizer. Вы также можете реализовать свой собственный дезинфектор атрибутов, чтобы контролировать значение других атрибутов в HTML. Создайте класс, реализующий AttributeSanitizerInterface и зарегистрируйте его как сервис. После этого используйте with_attribute_sanitizers, чтобы включить его для дезинфектора HTML:

1
2
3
4
5
6
7
8
9
10
11
12
# config/packages/html_sanitizer.yaml
framework:
    html_sanitizer:
        sanitizers:
            app.post_sanitizer:
                # ...
                with_attribute_sanitizers:
                    - App\Sanitizer\CustomAttributeSanitizer

                # вы также можете отключить ранее включенные пользовательские дезинфекторы атрибутов
                #without_attribute_sanitizers:
                #    - App\Sanitizer\CustomAttributeSanitizer