Защита от HTML-инъекций#
HTML-инъекция возникает, когда данные вставляются в HTML без обработки и браузер интерпретирует их как часть разметки. Это может изменить структуру страницы, нарушить работу интерфейса или привести к выполнению JavaScript-кода в браузере пользователя.
Перед вставкой в HTML необходимо обрабатывать данные, которые могли быть введены пользователем или получены из внешнего источника: значения полей, комментарии, описания, заголовки, подписи и другие текстовые данные. Способ обработки зависит от того, должны ли эти данные отображаться как обычный текст или как HTML-разметка.
Зачем нужна защита#
Защита от HTML-инъекций нужна, чтобы пользовательский ввод не мог изменить страницу или выполнить JavaScript-код в браузере пользователя системы. Такая проблема может возникнуть, если данные из поля, комментария, описания или внешнего источника вставляются в HTML без обработки.
Особенно опасны данные, которые сохраняются в БД и затем отображаются другим пользователям. В этом случае вредоносный HTML-фрагмент или JavaScript-код может срабатывать при открытии карточки, списка, описания или другого интерфейса, где используется сохраненное значение.
Варианты защиты#
В системе используются два основных способа защиты от HTML-инъекций.
Экранирование текстовых данных применяется, когда данные нужно вставить в HTML как обычный текст.
HTML-теги и JavaScript в таких данных не выполняются, а отображаются как текст.
Для экранирования используется
ru.bitec.app.btk.utils.HTMLUtils.escape.При формировании HTML через XML-литералы Scala можно использовать
scala.XML: значения, переданные как содержимое XML-узлов, экранируются автоматически.
Санитизация пользовательской HTML-разметки применяется, когда пользователю разрешено вводить HTML-разметку и ее нужно сохранить.
Допустимая разметка остается, а JavaScript, небезопасные элементы и ссылки на ресурсы, доступные не по HTTPS, удаляются.
Для санитизации используется
ru.bitec.app.btk.utils.HTMLUtils.sanitize.
Если поле предназначено для обычного текста, пользовательский ввод не должен интерпретироваться как HTML. Если поле предназначено для HTML-разметки, его нужно очищать от вредоносного содержимого через HTMLUtils.sanitize.
Экранирование текстовых данных#
Экранирование используется, когда данные нужно вывести внутри HTML как обычный текст: например, имя, заголовок, комментарий, подпись или другое текстовое значение. При экранировании специальные HTML-символы преобразуются в безопасное текстовое представление, поэтому браузер отображает введенную строку как текст, а не выполняет ее как разметку или JavaScript.
Для экранирования можно использовать HTMLUtils.escape. Если HTML формируется через XML-литералы Scala, можно использовать scala.XML, потому что значения внутри XML-узлов экранируются автоматически.
Пример экранирования текстовых данных
val sName = ropUser.get(_.sName) // интересное имя: "Ваня</h1><script>alert('XSS')</script><h1>"
/* Произошла HTML-инъекция:
<h1>Ваня</h1><script>alert('XSS')</script><h1></h1>
*/
val sHTML = s"""
<h1>$sName</h1>
"""
/* Значение будет выведено как обычный текст:
<h1>Ваня</h1><script>alert('XSS')</script><h1></h1>
*/
val sHTML_correct = s"""
<h1>${HTMLUtils.escape(sName)}</h1>
"""
/* scala.XML сама экранирует значения. Значение будет выведено как обычный текст:
<h1>Ваня</h1><script>alert('XSS')</script><h1></h1>
*/
val HTML = <h1>{sName}</h1>.toString
В первом варианте значение sName вставляется без обработки, поэтому браузер может интерпретировать часть строки как HTML. Во втором варианте значение экранируется через HTMLUtils.escape. В третьем варианте HTML формируется через scala.XML, который автоматически экранирует значения внутри XML-узлов.
Санитизация пользовательской HTML-разметки#
Санитизация используется, когда пользователю разрешено вводить HTML-разметку и эту разметку нужно сохранить. В этом случае обычное экранирование не подходит: оно превратит HTML-разметку в текст.
Для пользовательской HTML-разметки используйте HTMLUtils.sanitize. Метод очищает HTML от опасного содержимого и оставляет допустимую разметку.
HTMLUtils.sanitize:
удаляет JavaScript;
удаляет небезопасные HTML-элементы;
удаляет или очищает ссылки на ресурсы, доступные не по HTTPS.
Пример очистки HTML
/* HTML, введенный пользователем:
<img src="https://hips.hearstapps.com/clv.h-cdn.co/assets/16/18/gettyimages-586890581.jpg?crop=0.668xw:1.00xh;0.219xw,0&resize=980:*">
<img src="x" onError=alert('XSS')/>
*/
val sHTML = ropDoc.get(_.sDescription)
/* Безопасный для использования HTML:
<img src="https://hips.hearstapps.com/clv.h-cdn.co/assets/16/18/gettyimages-586890581.jpg?crop=0.668xw:1.00xh;0.219xw,0&resize=980:*">
<img src="x">
*/
val sHTML_correct = HTMLUtils.sanitize(sHTML)
В результате из HTML удаляется небезопасный обработчик события onError, а допустимые элементы остаются доступными для отображения.
Очистка HTML перед сохранением
Если HTML-значение хранится в БД и затем используется в интерфейсе, санитизацию можно выполнить при установке значения. В этом случае в БД сохраняется уже очищенный HTML.
override def setsDescription(rop: ApiRop, value: NString): Unit = {
super.setsDescription(rop, HTMLUtils.sanitize(value))
}
Такой вариант подходит для атрибутов, в которых по смыслу должна храниться пользовательская HTML-разметка. Если атрибут предназначен для обычного текста, не используйте санитизацию как замену экранированию: текстовые значения нужно безопасно выводить как текст.
Рекомендации для проектирования#
При проектировании полей и интерфейсов заранее определите, что должно храниться и отображаться:
обычный текст без HTML-разметки;
пользовательская HTML-разметка, для которой разрешено ограниченное форматирование.
В требованиях к новым полям, которые отображаются в HTML, необходимо явно указывать, допускается ли пользовательская HTML-разметка. От этого зависит способ обработки данных: текстовые данные нужно экранировать, а пользовательскую HTML-разметку — очищать через HTMLUtils.sanitize.
Если поле предназначено для обычного текста, пользовательский ввод не должен интерпретироваться как HTML. Если поле предназначено для HTML-разметки, необходимо предусмотреть очистку через HTMLUtils.sanitize до сохранения или перед отображением.
Правила безопасного использования#
Не вставляйте пользовательские данные в HTML без обработки. Даже если поле обычно содержит простой текст, оно может быть заполнено строкой с HTML-разметкой или JavaScript-кодом.
Не добавляйте необработанные пользовательские данные в HTML после вызова HTMLUtils.sanitize. Если после очистки строка снова изменяется и в нее добавляется пользовательский ввод, этот ввод также должен быть обработан.
Не используйте HTMLUtils.sanitize как универсальную замену безопасному выводу. Для текстовых значений используйте экранирование, а для HTML-разметки — санитизацию.
Примеры на этой странице относятся к вставке данных в HTML и обработке пользовательской HTML-разметки. Если данные используются внутри JavaScript, CSS, URL или другого контекста, способ безопасной обработки нужно определять отдельно для этого контекста.