Избегайте XSS и допускайте некоторые html-теги с JavaScript

У меня проблема в моем текущем проекте: Пользователи могут отправлять электронную почту с помощью текстового поля. Мы разрешаем пользователю вставлять все, что захотим, и, следовательно, некоторый HTML для форматирования. Например, пользователю должно быть разрешено использовать тэг <b> для жирного текста.

После заполнения электронной почты пользователь должен иметь возможность просматривать предварительный просмотр своей электронной почты динамически.

Есть небольшая проблема, хотя, как я могу избежать хакеров XSS, когда отображается предварительный просмотр?

Вы можете использовать их с помощью underscore.js, но это не форматирует предварительный просмотр.

Таким образом, я запретил все HTML-теги на данный момент и допускал только теги, такие как <hr>, <b> и т.д.

Что вы думаете об этом решении? Это достаточно безопасно?

Ответ 1

Чтобы предотвратить приложение из атак XSS, я обычно использую следующие правила:

  • Определите уровень безопасности для вашего приложения.
    Есть несколько инструментов, которые могут защитить ваше приложение, так как для меня лучшая безопасность обеспечивается OWASP tools: ESAPI или AntySami.
    Примечание. Использование Sanitization не гарантирует фильтрацию всего вредоносного кода, поэтому инструменты могут быть более или менее безопасными.

  • Поймите, нужно ли выполнять санитарную обработку на клиенте, сервере или на обеих сторонах. В большинстве случаев этого достаточно сделать на стороне сервера.

  • Поймите, нужно ли сохранять теги html (и какие теги вам нужно сохранить) или нет. Как было сказано ранее, не позволяя html-тэгам более безопасное решение.

На основании этого вы можете найти правильное решение.
1. Лично для санитарии кода сервера я использовал jSoup. Что касается меня, это очень хороший инструмент для этого.
Обычно для проверки входной уязвимости я использую следующий вектор:

';alert(String.fromCharCode(88,83,83))//\';alert(String.fromCharCode(88,83,83))//";alert(String.fromCharCode(88,83,83))//\";alert(String.fromCharCode(88,83,83))//--></SCRIPT>">'><SCRIPT>alert(String.fromCharCode(88,83,83))</SCRIPT>
  • Если вам нужно предотвратить XSS на стороне клиента, вы можете использовать следующие инструменты:
    a) JSSANItazer кажется немного устаревшим
    b) Dust - поддерживается твиттером;

Эти инструменты легко могут позволить вам дезинфицировать ваш вход и в основном отвечать на ваш вопрос.

Серверные инструменты, упомянутые выше.

Относительно третьей точки. Если вам не нужно обрабатывать теги html, вы можете легко использовать ESAPI на стороне сервера и ESAPI4JS на стороне клиента. Насколько я понимаю, это не сработает для вас.

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

Ответ 2

Вы всегда можете переключиться на BB-код, использовать тот же синтаксический анализатор для предварительного просмотра как форма, а затем проанализировать сервер ubb-кода сторона при отправке.

Смотрите эту статью, если вы хотите разобрать клиентскую сторону кода BB для предварительного просмотра и this для разбора серверной части BB-кода, предполагая, что вы отправляете письма с помощью PHP.

Ответ 3

Лучший способ избежать большинства атак XSS:

  • Отредактируйте свои данные, чтобы текст был правильно экранирован до того, как он попадет в html (вы можете создавать исключения для своих <b> и <hr> и других

  • Использование политики безопасности контента для отключения всех встроенных скриптов (также позволяет избежать средние атаки): http://www.html5rocks.com/en/tutorials/security/content-security-policy/

Эти два вместе сделают ваш сайт довольно надежным

Ответ 4

Я думаю, так как теги, которые работают в почтовых клиентах, являются относительно небольшим (но все еще значимым) списком, вы можете использовать белый список тегов, аналогичный тому, что вы делаете прямо сейчас. Было бы относительно сложным regext, чтобы вырезать теги, но те, которые вы разрешаете, но я думаю, что единственный способ разрешить некоторые теги, а не другие. Это не является надежным, хотя из-за того, что удаление тегов можно использовать для создания новых тегов, например, на моем входе ниже, предположим, что это запрещено, и вы его отключите:

<<script>script language="javascript">Do something bad</<script>script>

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

См. http://daringfireball.net/projects/markdown/

Таким образом, они могут использовать небольшое подмножество уценок форматирования, и вы можете заменить их на стороне сервера.

Ответ 5

Если вы хотите предотвратить атаку XSS на стороне сервера, разрешив некоторые теги, вы можете использовать OWASP HTMLSanitizer (антираность OWASP теперь неактивна), а также создавать свои собственные правила.

  • Страница проекта дезинфекции HTML:
    https://www.owasp.org/index.php/OWASP_Java_HTML_Sanitizer_Project
  • Пример правила - правило Ebay:
    https://github.com/OWASP/java-html-sanitizer/blob/master/src/main/java/org/owasp/html/examples/EbayPolicyExample.java

    https://github.com/OWASP/java-html-sanitizer/blob/master/src/main/java/org/owasp/html/examples/EbayPolicyExample.java
    public static final PolicyFactory POLICY_DEFINITION = new HtmlPolicyBuilder()
          .allowAttributes("id").matching(HTML_ID).globally()
          .allowAttributes("class").matching(HTML_CLASS).globally()
          .allowAttributes("lang").matching(Pattern.compile("[a-zA-Z]{2,20}"))
              .globally()
          .allowAttributes("title").matching(HTML_TITLE).globally()
          .allowStyling()
          .allowAttributes("align").matching(ALIGN).onElements("p")
          .allowAttributes("for").matching(HTML_ID).onElements("label")
          .allowAttributes("color").matching(COLOR_NAME_OR_COLOR_CODE)
              .onElements("font")
          .allowAttributes("face")
              .matching(Pattern.compile("[\\w;, \\-]+"))
              .onElements("font")
          .allowAttributes("size").matching(NUMBER).onElements("font")
          .allowAttributes("href").matching(ONSITE_OR_OFFSITE_URL)
              .onElements("a")
          .allowStandardUrlProtocols()
          .allowAttributes("nohref").onElements("a")
          .allowAttributes("name").matching(NAME).onElements("a")
          .allowAttributes(
              "onfocus", "onblur", "onclick", "onmousedown", "onmouseup")
              .matching(HISTORY_BACK).onElements("a")
          .requireRelNofollowOnLinks()
          .allowAttributes("src").matching(ONSITE_OR_OFFSITE_URL)
              .onElements("img")
          .allowAttributes("name").matching(NAME)
              .onElements("img")
          .allowAttributes("alt").matching(PARAGRAPH)
              .onElements("img")
          .allowAttributes("border", "hspace", "vspace").matching(NUMBER)
              .onElements("img")
          .allowAttributes("border", "cellpadding", "cellspacing")
              .matching(NUMBER).onElements("table")
          .allowAttributes("bgcolor").matching(COLOR_NAME_OR_COLOR_CODE)
              .onElements("table")
          .allowAttributes("background").matching(ONSITE_URL)
              .onElements("table")
          .allowAttributes("align").matching(ALIGN)
              .onElements("table")
          .allowAttributes("noresize").matching(Pattern.compile("(?i)noresize"))
              .onElements("table")
          .allowAttributes("background").matching(ONSITE_URL)
              .onElements("td", "th", "tr")
          .allowAttributes("bgcolor").matching(COLOR_NAME_OR_COLOR_CODE)
              .onElements("td", "th")
          .allowAttributes("abbr").matching(PARAGRAPH)
              .onElements("td", "th")
          .allowAttributes("axis", "headers").matching(NAME)
              .onElements("td", "th")
          .allowAttributes("scope")
              .matching(Pattern.compile("(?i)(?:row|col)(?:group)?"))
              .onElements("td", "th")
          .allowAttributes("nowrap")
              .onElements("td", "th")
          .allowAttributes("height", "width").matching(NUMBER_OR_PERCENT)
              .onElements("table", "td", "th", "tr", "img")
          .allowAttributes("align").matching(ALIGN)
              .onElements("thead", "tbody", "tfoot", "img",
                               "td", "th", "tr", "colgroup", "col")
          .allowAttributes("valign").matching(VALIGN)
              .onElements("thead", "tbody", "tfoot",
                              "td", "th", "tr", "colgroup", "col")
          .allowAttributes("charoff").matching(NUMBER_OR_PERCENT)
              .onElements("td", "th", "tr", "colgroup", "col",
                              "thead", "tbody", "tfoot")
          .allowAttributes("char").matching(ONE_CHAR)
              .onElements("td", "th", "tr", "colgroup", "col",
                               "thead", "tbody", "tfoot")
          .allowAttributes("colspan", "rowspan").matching(NUMBER)
              .onElements("td", "th")
          .allowAttributes("span", "width").matching(NUMBER_OR_PERCENT)
              .onElements("colgroup", "col")
          .allowElements(
              "a", "label", "noscript", "h1", "h2", "h3", "h4", "h5", "h6",
              "p", "i", "b", "u", "strong", "em", "small", "big", "pre", "code",
              "cite", "samp", "sub", "sup", "strike", "center", "blockquote",
              "hr", "br", "col", "font", "map", "span", "div", "img",
              "ul", "ol", "li", "dd", "dt", "dl", "tbody", "thead", "tfoot",
              "table", "td", "th", "tr", "colgroup", "fieldset", "legend")
          .toFactory();