
bluemonday: быстрый HTML-санитайзер на golang (вдохновлённый OWASP Java HTML Sanitizer) для очистки пользовательского контента от XSS
bluemonday — это HTML-санитайзер, реализованный на Go. Он быстрый и легко настраивается.
bluemonday принимает недоверенный пользовательский контент в качестве входных данных и возвращает HTML, очищенный по белому списку разрешённых HTML-элементов и атрибутов, чтобы вы могли безопасно включать такой контент на свою веб-страницу.
Если вы принимаете пользовательский контент, а ваш сервер написан на Go, вам нужен bluemonday.
Политика по умолчанию для пользовательского контента (bluemonday.UGCPolicy().Sanitize()) превращает это:
Hello <STYLE>.XSS{background-image:url("javascript:alert('XSS')");}</STYLE><A CLASS=XSS></A>World
в безвредное:
Hello World
А это:
<a href="javascript:alert('XSS1')" onmouseover="alert('XSS2')">XSS<a>
превращает в это:
XSS
При этом продолжает разрешать вот это:
<a href="http://www.google.com/">
<img src="https://ssl.gstatic.com/accounts/ui/logo_2x.png"/>
</a>
Пропускает почти без изменений (к нему добавился атрибут rel="nofollow", что хорошо для пользовательского контента):
<a href="http://www.google.com/" rel="nofollow">
<img src="https://ssl.gstatic.com/accounts/ui/logo_2x.png"/>
</a>
Он защищает сайты от атак XSS. Существует множество векторов XSS-атак, и лучший способ снизить риск — очищать пользовательский ввод по известному безопасному списку HTML-элементов и атрибутов.
bluemonday следует всегда запускать после любой другой обработки.
Если вы используете blackfriday или Pandoc, то bluemonday следует запускать после этих шагов. Это гарантирует, что незащищённый HTML не появится позже в вашем процессе.
bluemonday во многом вдохновлён как OWASP Java HTML Sanitizer, так и HTML Purifier.
Основан на белом списке: вам нужно либо построить политику, описывающую HTML-элементы и атрибуты для разрешения (и regexp-шаблоны атрибутов), либо использовать одну из предоставленных политик с хорошими настройками по умолчанию.
Политика, содержащая белый список, применяется с помощью быстрого невалидирующего, однопроходного токенизирующего парсера, реализованного в библиотеке Go net/html основной командой Go.
Мы ожидаем корректно оформленный HTML (закрывающие элементы для каждого открытого элемента, правильная вложенность) и поэтому не занимаемся исправлением плохо вложенного или неполного HTML. Мы сосредоточены на том, чтобы все существующие элементы были описаны в белом списке политики, а атрибуты и ссылки были безопасны для использования на вашей веб-странице. Здесь действует принцип GIGO: если вы скормите ему плохой HTML, bluemonday не обязан придумывать, как сделать его снова хорошим.
Да
Мы используем bluemonday в продакшене, перейдя с широко используемого и многократно проверенного в полевых условиях OWASP Java HTML Sanitizer.
Мы проходим наш обширный набор тестов (включая тесты AntiSamy, а также тесты на любые поднятые задачи). Проверьте нерешённые задачи, чтобы понять, не может ли что-то стать для вас препятствием.
Мы приглашаем pull request'ы и issues, чтобы помочь нам гарантировать всестороннюю защиту от различных атак через пользовательский контент.
Установите с помощью go get github.com/microcosm-cc/bluemonday
Затем вызовите его:
package main
import (
"fmt"
"github.com/microcosm-cc/bluemonday"
)
func main() {
// Do this once for each unique policy, and use the policy for the life of the program
// Policy creation/editing is not safe to use in multiple goroutines
p := bluemonday.UGCPolicy()
// The policy can then be used to sanitize lots of input and it is safe to use the policy in multiple goroutines
html := p.Sanitize(
`<a onblur="alert(secret)" href="http://www.google.com">Google</a>`,
)
// Output:
// <a href="http://www.google.com" rel="nofollow">Google</a>
fmt.Println(html)
}
Мы предлагаем три способа вызова Sanitize:
p.Sanitize(string) string
p.SanitizeBytes([]byte) []byte
p.SanitizeReader(io.Reader) bytes.Buffer
Если вы одержимы производительностью, p.SanitizeReader(r).Bytes() вернёт []byte без выполнения лишних преобразований входов или выходов. Хотя разница настолько незначительна, что вам никогда не придётся об этом беспокоиться.
Вы можете создавать собственные политики:
package main
import (
"fmt"
"github.com/microcosm-cc/bluemonday"
)
func main() {
p := bluemonday.NewPolicy()
// Require URLs to be parseable by net/url.Parse and either:
// mailto: http:// or https://
p.AllowStandardURLs()
// We only allow <p> and <a href="">
p.AllowAttrs("href").OnElements("a")
p.AllowElements("p")
html := p.Sanitize(
`<a onblur="alert(secret)" href="http://www.google.com">Google</a>`,
)
// Output:
// <a href="http://www.google.com">Google</a>
fmt.Println(html)
}
Мы поставляем две политики по умолчанию:
bluemonday.StrictPolicy() — её можно считать эквивалентом удаления всех HTML-элементов и их атрибутов, поскольку в её белом списке ничего нет. Пример сценария использования — заголовки записей в блоге, где HTML-теги не ожидаются вовсе, а если они есть, то и элементы, и их содержимое должны быть удалены. Это очень строгая политика.bluemonday.UGCPolicy() — допускает широкий набор HTML-элементов и атрибутов, безопасных для пользовательского контента. Обратите внимание: эта политика не разрешает iframe, object, embed, стили, script и т.д. Пример сценария использования — тело записи в блоге, где ожидается разнообразное форматирование, а также возможны TABLE и IMG.Суть построения политики — определить, какие HTML-элементы и атрибуты считаются безопасными для вашего сценария. OWASP предоставляет шпаргалку по предотвращению XSS для объяснения рисков, но по сути:
script, style, iframe, object, embed, base, которые позволяют клиенту выполнять код или включать сторонний контент, способный выполнять кодПо сути, вы должны быть в состоянии описать, какой HTML допустим для вашего сценария. Если вы не уверены, что можете описать свою политику, подумайте об использовании одной из поставляемых политик, например bluemonday.UGCPolicy().
Чтобы создать новую политику:
p := bluemonday.NewPolicy()
Чтобы добавить элементы в политику, добавьте только элементы:
p.AllowElements("b", "strong")
Или используйте регулярное выражение:
Примечание: если элемент добавляется по имени, как показано выше, любое подходящее регулярное выражение будет проигнорировано
Также рекомендуется следить, чтобы несколько шаблонов не перекрывались, поскольку порядок выполнения не гарантируется и это может привести к пропуску некоторых правил.
p.AllowElementsMatching(regex.MustCompile(`^my-element-`))
Или добавляйте элементы путём добавления атрибута:
// Note the recommended pattern, see the recommendation on using .Matching() below
p.AllowAttrs("nowrap").OnElements("td", "th")