Как запретить людям выполнять XSS в Spring MVC?

Что мне делать, чтобы предотвратить использование XSS в Spring MVC? Прямо сейчас я просто помещаю все места, где я выводю пользовательский текст, в теги JSTL <c:out> или fn:escapeXml() функции, но это кажется подверженным ошибкам, поскольку я могу пропустить место.

Есть ли простой систематический способ предотвратить это? Может как фильтр или что-то в этом роде? Я собираю ввод, задав @RequestParam параметров в моих методах контроллера.


person Doug    schedule 27.01.2010    source источник
comment
См. Статью Джоэла Спольски Как сделать код неправильным - joelonsoftware.com/articles/ Wrong.html, в котором рассказывается, как решить именно эту проблему.   -  person mob    schedule 27.01.2010
comment
Заголовок может быть неправильно прочитан, и сначала нужно ответить, установив JVM в мозг человека. ;)   -  person Svante    schedule 31.01.2010
comment
Очень похожий вопрос можно найти здесь: stackoverflow.com/questions/2658922 / xss-Prevention-in-java.   -  person Brad Parks    schedule 17.07.2012
comment
Я написал сообщение в блоге о том, как отфильтровать XSS-уязвимости для Jersey REST API. Достаточно легко сопоставить это со стандартным фильтром Java: codehustler.org/   -  person Alessandro Giannone    schedule 15.05.2013


Ответы (8)


В Spring вы можете экранировать HTML-код со страниц JSP, созданных тегами <form>. Это перекрывает множество путей для XSS-атак и может быть выполнено автоматически тремя способами:

Для всего приложения в файле web.xml:

<context-param>
    <param-name>defaultHtmlEscape</param-name>
    <param-value>true</param-value>
</context-param>

Для всех форм на данной странице в самом файле:

<spring:htmlEscape defaultHtmlEscape="true" /> 

Для каждой формы:

<form:input path="someFormField" htmlEscape="true" /> 
person Tendayi Mawushe    schedule 27.01.2010
comment
Я помещаю ‹spring: htmlEscape defaultHtmlEscape = true /› во включаемый файл, который я включаю во все свои страницы, но, похоже, это не имеет значения. Приведет ли этот тег к экранированию $ {param.q}? - person Doug; 27.01.2010
comment
Если вы используете результирующие строки внутри атрибута HTML или Javascript, defaultHTMLEscape недостаточно, тогда используйте <c:out />-тег. Похоже, что defaultHtmlEscape не экранирует все символы html. Он убегает, например. '‹' '›' Или '&', но двойные кавычки " не были экранированы для меня. Это может привести к проблемам, если будет использована результирующая строка, например. в html-атрибуте или javascript. (См. Также ответ Эрленда) - person Andreas; 21.05.2010
comment
Я добавляю контекстный параметр в свой web.xml, но он не работает. - person Jack; 05.08.2015
comment
Это для кодирования ввода или экранирования вывода? - person pinkpanther; 22.07.2016

Я использую Hibernate Validator через @Valid для всех входных объектов (привязка и @RequestBody json, см. https://dzone.com/articles/spring-31-valid-requestbody). Так что @org.hibernate.validator.constraints.SafeHtml - хорошее решение для меня.

Hibernate SafeHtmlValidator зависит от org.jsoup, поэтому необходимо добавить еще одну зависимость проекта:

<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.10.1</version>
</dependency>

Для bean User с полем

@NotEmpty
@SafeHtml
protected String name;

для попытки обновления со значением <script>alert(123)</script> в контроллере

@PutMapping(value = "/{id}", consumes = MediaType.APPLICATION_JSON_VALUE)
public void update(@Valid @RequestBody User user, @PathVariable("id") int id) 

or

@PostMapping
public void createOrUpdate(@Valid User user) {

выбрасывается BindException для привязки и MethodArgumentNotValidException для @RequestBody с сообщением по умолчанию:

name may have unsafe html content

Валидатор также работает для привязки, как и до сохранения. Приложения можно протестировать на странице http://topjava.herokuapp.com/.

ОБНОВЛЕНИЕ: см. Также комментарий от @GuyT

CVE-2019-10219 и статус @SafeHtml

Нам сообщили об ошибке CVE-2019-10219, связанной с ограничением @SafeHtml, и она была исправлена ​​как в 6.0.18.Final, так и в 6.1.0.Final ....

Однако мы пришли к выводу, что ограничение @SafeHtml было хрупким, очень чувствительным к безопасности и зависело от внешней библиотеки, которая не была предназначена для этой цели. Включение его в ядро ​​Hibernate Validator было не очень хорошей идеей. Вот почему мы устарели и отметили его для удаления. Здесь нет никакого волшебного плана, поэтому нашим пользователям придется поддерживать это ограничение самостоятельно.

Резюме для себя: это безопасно и можно использовать, пока не будет найдено лучшее решение.

person Grigory Kislin    schedule 16.11.2016
comment
Обратите внимание, что сейчас это не рекомендуется из-за проблем с безопасностью stackoverflow.com/questions/58913428/ - person GuyT; 20.04.2020

Попробуйте XSSFilter.

person Bozho    schedule 27.01.2010
comment
Мое приложение перестало работать после настройки для xssflt.jar. Как только я удалил фильтр и сопоставление фильтров из веб-xml, он начал работать. - person Manish Mahajan; 19.04.2014
comment
Как я могу использовать это в проекте на основе Maven? - person Jack; 05.08.2015
comment
он не работает для типа формы multipart. Это означает, что, когда мы отправляем файлы с формой, эта фильтрация не работает. Правильно ли мой запрос? Если да, есть ли решение? Или что со мной не так - person Dhanu K; 21.05.2018

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

У меня есть пример этого здесь: http://erlend.oftedal.no/blog/?blogid=91

Также ознакомьтесь со шпаргалкой по предотвращению ошибок OWASP XSS: http://www.owasp.org/index.php/XSS_%28Cross_Site_Scripting%29_Prevention_Cheat_Sheet

Итак, краткий ответ: убедитесь, что вы избегаете вывода, как это предлагает Тендаи Мавуше, но будьте особенно осторожны при выводе данных в атрибутах HTML или javascript.

person Erlend    schedule 31.01.2010
comment
будьте особенно осторожны = используйте <c:out />-тег при выводе данных в атрибутах HTML или javascript. (см. мой комментарий в ответе Тендаи Мавуше) - person Andreas; 21.05.2010
comment
Что ж, это больше, чем просто использование ‹c: out /›. В Javascript есть управляющие символы, отличные от HTML, и его можно атаковать по-разному, поэтому необходимо обрабатывать его с помощью других типов экранирования. Простой пример: var a = '‹c: out ... /›'; var b = '‹c: out ... /›'; Если ввод в a представляет собой одиночную обратную косую черту, сценарий в b будет запущен. Экранирование зависит от контекста. - person Erlend; 24.05.2010

Как вы в первую очередь собираете вводимые пользователем данные? Этот вопрос / ответ может помочь, если вы используете FormController:

Spring: экранирование ввода при привязке к команде

person Ben    schedule 27.01.2010

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

Обзор: http://www.gablog.eu/online/node/91

person sibidiba    schedule 27.01.2010

Вместо того, чтобы полагаться только на <c:out />, следует также использовать библиотеку antixss, которая не только кодирует, но и дезинфицирует входящие вредоносные скрипты. Одна из лучших доступных библиотек - OWASP Antisamy, она очень гибкая и может быть настроена (с использованием файлов политики xml) в соответствии с требованиями.

Например, если приложение поддерживает только ввод текста, тогда наиболее общий файл политики, предоставленный OWASP, который очищает и удаляет большинство тегов html. Точно так же, если приложение поддерживает редакторы HTML (например, tinymce), которым нужны все виды тегов HTML, можно использовать более гибкую политику, например файл правил ebay

person rjha    schedule 11.06.2015

**To avoid XSS security threat in spring application**

Решение проблемы XSS состоит в том, чтобы отфильтровать все текстовые поля в форме во время отправки формы.

    It needs XML entry in the web.xml file & two simple classes.

        java code :-
        The code for the  first class named CrossScriptingFilter.java is :

        package com.filter;

        import java.io.IOException;
        import javax.servlet.Filter;
        import javax.servlet.FilterChain;
        import javax.servlet.FilterConfig;
        import javax.servlet.ServletException;
        import javax.servlet.ServletRequest;
        import javax.servlet.ServletResponse;
        import javax.servlet.http.HttpServletRequest;
        import org.apache.log4j.Logger;

        public class CrossScriptingFilter implements Filter {
            private static Logger logger = Logger.getLogger(CrossScriptingFilter.class);
            private FilterConfig filterConfig;

            public void init(FilterConfig filterConfig) throws ServletException {
                this.filterConfig = filterConfig;
            }

            public void destroy() {
                this.filterConfig = null;
            }

            public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                throws IOException, ServletException {
                logger.info("Inlter CrossScriptingFilter  ...............");
                chain.doFilter(new RequestWrapper((HttpServletRequest) request), response);
                logger.info("Outlter CrossScriptingFilter ...............");
            }

        }

Второй класс кода с именем RequestWrapper.java:

пакет com.filter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import org.apache.log4j.Logger;

public final class RequestWrapper extends HttpServletRequestWrapper {
    private static Logger logger = Logger.getLogger(RequestWrapper.class);
    public RequestWrapper(HttpServletRequest servletRequest) {
        super(servletRequest);
    }

    public String[] getParameterValues(String parameter) {
        logger.info("InarameterValues .. parameter .......");
        String[] values = super.getParameterValues(parameter);
        if (values == null) {
            return null;
        }
        int count = values.length;
        String[] encodedValues = new String[count];
        for (int i = 0; i < count; i++) {
            encodedValues[i] = cleanXSS(values[i]);
        }
        return encodedValues;
    }

    public String getParameter(String parameter) {
        logger.info("Inarameter .. parameter .......");
        String value = super.getParameter(parameter);
        if (value == null) {
            return null;
        }
        logger.info("Inarameter RequestWrapper ........ value .......");
        return cleanXSS(value);
    }

    public String getHeader(String name) {
        logger.info("Ineader .. parameter .......");
        String value = super.getHeader(name);
        if (value == null)
            return null;
        logger.info("Ineader RequestWrapper ........... value ....");
        return cleanXSS(value);
    }

    private String cleanXSS(String value) {
        // You'll need to remove the spaces from the html entities below
        logger.info("InnXSS RequestWrapper ..............." + value);
        //value = value.replaceAll("<", "& lt;").replaceAll(">", "& gt;");
        //value = value.replaceAll("\\(", "& #40;").replaceAll("\\)", "& #41;");
        //value = value.replaceAll("'", "& #39;");
        value = value.replaceAll("eval\\((.*)\\)", "");
        value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");

        value = value.replaceAll("(?i)<script.*?>.*?<script.*?>", "");
        value = value.replaceAll("(?i)<script.*?>.*?</script.*?>", "");
        value = value.replaceAll("(?i)<.*?javascript:.*?>.*?</.*?>", "");
        value = value.replaceAll("(?i)<.*?\\s+on.*?>.*?</.*?>", "");
        //value = value.replaceAll("<script>", "");
        //value = value.replaceAll("</script>", "");
        logger.info("OutnXSS RequestWrapper ........ value ......." + value);
        return value;
    }

Осталась только запись XML в файле web.xml:

        <filter>
        <filter-name>XSS</filter-name>
        <display-name>XSS</display-name>
        <description></description>
        <filter-class>com.filter.CrossScriptingFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>XSS</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

/ * указывает, что для каждого запроса, сделанного из браузера, он будет вызывать класс CrossScriptingFilter. Что проанализирует все компоненты / элементы, полученные из запроса, и заменит все теги javascript, помещенные хакером, пустой строкой, т.е.

person GAURAV KUMAR GUPTA    schedule 27.10.2017
comment
ваш код - плохой образец, чтобы избежать XSS. Вам не нужно делать черный список с подходом замены. Вам нужно закодировать вывод, чтобы действительно бороться с XSS! - person SPoint; 19.10.2018
comment
Внесение вашего ввода в черный список, как представлено в этом ответе, как также сказал @SPoint, - это плохой подход. Вам нужно двухстороннее решение: 1 выход для выхода и 2 входа в белый список. Белый список ограничивает то, что пользователю разрешено отправлять, в отличие от черного списка, где вы разрешаете что угодно и надеетесь очистить ввод самостоятельно. - person GabiM; 25.02.2019