Предотвращение XSS в веб-приложении JSP / Servlet

Как я могу предотвратить атаки XSS в веб-приложении JSP / Servlet?


person newbie    schedule 17.04.2010    source источник
comment
Там размещен отличный пост о том, как предотвратить атаки XSS в различных ситуациях: stackoverflow.com/questions/19824338/   -  person user1459144    schedule 13.11.2013


Ответы (9)


XSS можно предотвратить в JSP, используя JSTL _ 1_ тег или _ 2_ Функция EL при (повторном) отображении ввода, управляемого пользователем. Сюда входят параметры запроса, заголовки, файлы cookie, URL, тело и т. Д. Все, что вы извлекаете из объекта запроса. Кроме того, во время повторного отображения необходимо экранировать управляемый пользователем ввод из предыдущих запросов, который хранится в базе данных.

Например:

<p><c:out value="${bean.userControlledValue}"></p>
<p><input name="foo" value="${fn:escapeXml(param.foo)}"></p>

Это приведет к экранированию символов, которые могут искажать отображаемый HTML, например <, >, ", ' и &, в HTML / XML. объекты, такие как &lt;, &gt;, &quot;, &apos; и &amp;.

Обратите внимание, что вам не нужно экранировать их в коде Java (сервлета), поскольку там они безвредны. Некоторые могут избежать их во время обработки запроса (как вы делаете в сервлете или фильтре) вместо обработки ответа (как в JSP), но таким образом вы можете рискнуть что данные излишне экранированы с двойным экранированием (например, & становится &amp;amp; вместо &amp;, и в конечном итоге конечный пользователь увидит &amp; представление), или что данные, хранящиеся в БД, становятся непереносимыми (например, при экспорте данных в JSON, CSV, XLS, PDF, и т. д., который вообще не требует экранирования HTML). Вы также потеряете социальный контроль, потому что больше не знаете, что на самом деле ввел пользователь. Как администратор сайта, вы бы действительно хотели знать, какие пользователи / IP-адреса пытаются выполнить XSS, чтобы вы могли легко отслеживать их и предпринять соответствующие действия. Экранирование во время обработки запроса должно использоваться только и только в качестве последнего средства, когда вам действительно нужно как можно быстрее исправить крушение поезда плохо разработанного устаревшего веб-приложения. Тем не менее, вы должны в конечном итоге переписать файлы JSP, чтобы они стали безопасными для XSS.

Если вы хотите повторно отображать ввод, управляемый пользователем, как HTML, в котором вы хотите разрешить только определенное подмножество HTML-тегов, например <b>, <i>, <u> и т. Д., То вам необходимо очистить ввод с помощью белого списка. Для этого вы можете использовать анализатор HTML, например Jsoup. Но гораздо лучше ввести удобный для человека язык разметки, такой как Markdown (также используется здесь, в Stack Overflow). Затем вы можете использовать для этого парсер Markdown, например CommonMark. Он также имеет встроенные возможности очистки HTML. См. Также Markdown или HTML.

Единственная проблема на стороне сервера в отношении баз данных - это SQL-инъекция. Вам необходимо убедиться, что вы никогда не объединяете строки, управляемые пользователем, прямо в запросе SQL или JPQL и полностью используете параметризованные запросы. В терминах JDBC это означает, что вы должны использовать PreparedStatement вместо Statement. В терминах JPA используйте Query.


Альтернативой может быть переход с JSP / сервлета на структуру Java EE MVC JSF. Он имеет встроенную защиту от XSS (и CSRF!) Повсюду. См. Также Предотвращение атак CSRF, XSS и SQL-инъекций в JSF .

person BalusC    schedule 17.04.2010
comment
@chad: это неправда. Это только тот случай, когда вы объединяете строки, контролируемые пользователем, прямо в запросе SQL / HQL / JPQL, например, "SELECT ... WHERE SOMEVAL = " + someval вместо использования параметризованных запросов, как вы показали. Ни один ORM не может защитить от подобных ошибок разработчика. - person BalusC; 11.02.2012
comment
@BalusC - Да! Я все поменял. Уязвимый пример: Query query = session.createQuery (SELECT * FROM TABLE WHERE SOMEVAL = + someval); Использование синтаксиса привязки: в Hibernate (как в моем примере выше) предотвращает SQL-инъекцию. Удаление комментария, чтобы никто не использовал мой плохой пример. - person chadmaughan; 11.02.2012
comment
Я думаю, вам НЕОБХОДИМО выполнить проверку на сервере. Всю проверку можно обойти, изменив параметры HTTP. А иногда сохраняемые данные могут использоваться другими приложениями в корпоративном приложении. Иногда у вас нет доступа к представлениям других приложений, поэтому вам нужно очистить ввод перед сохранением в базе данных. - person Guido Celada; 09.10.2014
comment
@Guido: ты не понимаешь проблемы. - person BalusC; 09.10.2014
comment
@BalusC: пожалуйста, расширьте. - person Guido Celada; 09.10.2014
comment
@BalusC: Привет, Бал, используя ваше предложение, я удалил 4 проблемы с XSS в моем приложении, большое спасибо за полезное решение. Здесь мне снова нужно ваше предложение, поскольку я показываю объект dom с помощью jstl espression, например <div><c:out value="${htmlContent}" escapeXml="false" /></div> здесь htmlContentvalue - это что-то вроде <span><h1>Hi</h1></span> (это значение, поступающее из базы данных), теперь, если я удалю escapeXml="false" из c:out, тогда он отобразится как на странице, тогда если продолжите escapeXml="false" его синтаксический анализ объект dom правильно, но когда мой htmlContent имеет некоторый код сценария, возникает проблема xss. - person Venkaiah Yepuri; 04.01.2017
comment
@BalusC: если htmlContent равно <span><h1><script>alert("Hi");</script></h1></span>, тогда, если я использую <c:out value="${htmlContent}" escapeXml="false" /></div> в jsp, тогда в браузере пользователь получит окно предупреждения, поэтому это может привести к проблеме XSS. Пожалуйста, посоветуйте мне, что я могу сделать в этой ситуации. - person Venkaiah Yepuri; 04.01.2017
comment
@Venki: Просто прочтите 4-й абзац ответа, в котором рассказывается о работе с управляемым пользователем HTML. - person BalusC; 04.01.2017
comment
Отличный ответ @BalusC Большое спасибо! Быстрый вопрос, если вы не против. Значит, я не должен экранировать html при вводе пользователем, только при его отображении? Это то, что вы подразумеваете под двойным экранированием? А если нет, то как избежать ввода пользователя с помощью текстового поля, а не поля ввода? Правильно ли (htmlEscape = true)? - person Jonathan Laliberte; 22.07.2017
comment
Обратите внимание: кодировка объекта HTML не работает, если вы помещаете ненадежные данные в тег ‹script› где угодно, или в атрибут обработчика событий, например onmouseover, или внутри CSS, или в URL-адрес. github.com/OWASP/CheatSheetSeries/blob/main/ а> - person peater; 06.06.2019
comment
@peater: Ага, при помещении ненадежных данных в JS-код вам нужно JS-кодировать вместо HTML-кодирования. И, помещая ненадежные данные в код CSS, вам нужно кодировать CSS вместо HTML-кодирования. И, помещая ненадежные данные в URL-адреса, вам необходимо кодировать URL вместо HTML-кодирования. Кодировку HTML следует использовать только для помещения ненадежных данных в код HTML. - person BalusC; 06.06.2019
comment
Чтобы добавить сюда некоторую информацию, для свойства escapeXml по умолчанию в теге c: out установлено значение true. - person Raphael Onofre; 01.10.2019

How-to-prevent-xss спрашивали несколько раз. Вы найдете много информации в StackOverflow. Кроме того, на веб-сайте OWASP есть шпаргалка по предотвращению XSS-атак, которую вам следует изучить .

Что касается используемых библиотек, библиотека ESAPI OWASP имеет разновидность java. . Тебе стоит попробовать это. Кроме того, каждый фреймворк, который вы используете, имеет некоторую защиту от XSS. Опять же, на веб-сайте OWASP есть информация о наиболее популярных фреймворках, поэтому я бы порекомендовал пройтись по их сайту.

person Sripathi Krishnan    schedule 17.04.2010
comment
Шпаргалки по OWASP перемещены на GitHub. Вот ссылка на шпаргалку по предотвращению XSS-атак github.com/OWASP/ CheatSheetSeries / blob / master / cheatsheets / - person peater; 06.06.2019

Мне очень повезло с OWASP Anti-Samy и советником AspectJ на всех моих контроллерах Spring, который блокирует проникновение XSS.

public class UserInputSanitizer {

    private static Policy policy;
    private static AntiSamy antiSamy;

    private static AntiSamy getAntiSamy() throws PolicyException  {
        if (antiSamy == null) {
            policy = getPolicy("evocatus-default");
            antiSamy = new AntiSamy();
        }
        return antiSamy;

    }

    public static String sanitize(String input) {
        CleanResults cr;
        try {
            cr = getAntiSamy().scan(input, policy);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return cr.getCleanHTML();
    }

    private static Policy getPolicy(String name) throws PolicyException {
        Policy policy = 
            Policy.getInstance(Policy.class.getResourceAsStream("/META-INF/antisamy/" + name + ".xml"));
        return policy;
    }

}

Вы можете получить советник AspectJ из этого сообщения stackoverflow < / а>

Я думаю, что это лучший подход, чем c: out, особенно если вы много используете javascript.

person Adam Gent    schedule 31.01.2011
comment
Обычной практикой является экранирование любых данных, управляемых пользователем, во время повторного отображения, а не во время обработки отправленных данных в сервлете или во время сохранения в БД. Если вы HTML-экранируете его во время обработки отправленных данных и / или сохранения в БД, тогда все это распространяется на бизнес-код и / или в базу данных. Это только проблемы с обслуживанием, и вы рискуете дважды сбежать или даже больше, когда будете делать это в разных местах. Бизнес-код и БД, в свою очередь, нечувствительны к XSS. Только вид есть. Тогда вы должны убежать от него только прямо здесь, на виду. - person Shubham Maheshwari; 29.08.2015
comment
И да и нет. Хотя общепринятая практика заключается в том, чтобы убежать на всеобщее обозрение, есть много причин, по которым вам может потребоваться дезинфекция при записи. В некоторых случаях вы действительно хотите, чтобы ваши пользователи вводили подмножество HTML, и хотя вы можете дезинфицировать отображение, на самом деле это довольно медленно и даже сбивает с толку пользователей. Во-вторых, если вы делитесь данными со сторонними службами, такими как внешние API, эти службы могут или не могут выполнять надлежащую дезинфекцию. - person Adam Gent; 30.08.2015
comment
как мы с вами оба упомянули, нормальная практика - убежать на всеобщее обозрение. То, что вы упомянули в своем комментарии выше, является более конкретными вариантами использования и, следовательно, требует конкретных решений. - person Shubham Maheshwari; 30.09.2015
comment
Да, мне, возможно, следует прояснить мой вариант использования. Я работаю в основном над вопросами управления контентом (редактирование HTML). - person Adam Gent; 30.09.2015

Для управления XSS требуется несколько проверок данных со стороны клиента.

  1. Проверка ввода (проверка формы) на стороне сервера. Есть несколько способов добиться этого. Вы можете попробовать проверку bean-компонентов JSR 303 (hibernate validator ) или Платформа проверки ввода ESAPI. Хотя я сам (пока) не пробовал, есть аннотация, которая проверяет безопасный HTML (@SafeHtml). Фактически вы можете использовать валидатор Hibernate с Spring MVC для проверки bean-компонентов -> Ссылка
  2. Экранирование запросов URL. Для всех ваших HTTP-запросов используйте какой-то фильтр XSS. Я использовал следующее для нашего веб-приложения, и оно заботится об очистке запроса URL-адреса HTTP: http://www.servletsuite.com/servlets/xssflt.htm
  3. Экранирование данных / HTML, возвращенных клиенту (см. объяснение @BalusC выше).
person MasterV    schedule 30.10.2012

Я бы посоветовал регулярно тестировать уязвимости с помощью автоматизированного инструмента и исправлять все, что он находит. Намного проще предложить библиотеку для устранения конкретной уязвимости, чем для всех XSS-атак в целом.

Skipfish - это инструмент с открытым исходным кодом от Google, который я исследовал: он находит довольно много всего, и, кажется, стоит использовать.

person Sean Reilly    schedule 17.04.2010
comment
Профилактика лучше, чем диагностика (например, скипфиш) с последующими быстрыми исправлениями. - person Sripathi Krishnan; 17.04.2010
comment
Я не согласен. Профилактика без диагностики - это просто догма. Выполняйте диагностику как часть цикла CI, чтобы избежать проблемы с быстрым исправлением. - person Sean Reilly; 17.04.2010

Нет простого готового решения против XSS. OWASP ESAPI API имеет некоторую поддержку экранирования, что очень полезно, и у них есть библиотеки тегов.

Мой подход заключался в том, чтобы расширить теги stuts 2 следующими способами.

  1. Измените тег s: property, чтобы он мог принимать дополнительные атрибуты, указывающие, какой тип экранирования требуется (escapeHtmlAttribute = "true" и т. Д.). Это включает в себя создание новых классов Property и PropertyTag. Класс Property использует OWASP ESAPI api для экранирования.
  2. Измените шаблоны freemarker, чтобы использовать новую версию s: property, и установите экранирование.

Если вы не хотели изменять классы на шаге 1, можно было бы импортировать теги ESAPI в шаблоны freemarker и при необходимости уйти. Затем, если вам нужно использовать тег s: property в вашем JSP, оберните его тегом и ESAPI.

Я написал здесь более подробное объяснение.

http://www.nutshellsoftware.org/software/securing-struts-2-using-esapi-part-1-securing-outputs/

Я согласен, что экранирование входных данных не идеально.

person brett.carr    schedule 13.05.2011

Мое личное мнение таково, что вам следует избегать использования страниц JSP / ASP / PHP / etc. Вместо этого выводите в API, похожий на SAX (предназначенный только для вызова, а не для обработки). Таким образом, создается единственный слой, который должен создавать хорошо сформированный вывод.

person Tom Hawtin - tackline    schedule 19.04.2010

Если вы хотите автоматически экранировать все переменные JSP без явного переноса каждой переменной, вы можете использовать преобразователь EL , как подробно описано здесь с полным исходным кодом и примером (JSP 2.0 или новее), и обсуждается более подробно здесь:

Например, при использовании вышеупомянутого преобразователя EL ваш код JSP останется таким, но каждая переменная будет автоматически экранирована преобразователем

...
<c:forEach items="${orders}" var="item">
  <p>${item.name}</p>
  <p>${item.price}</p>
  <p>${item.description}</p>
</c:forEach>
...

Если вы хотите принудительно экранировать по умолчанию в Spring, вы также можете рассмотреть это, но это не экранирует выражения EL, а просто вывод тегов, я думаю:

http://forum.springsource.org/showthread.php?61418-Spring-cross-site-scripting&p=205646#post205646

Примечание. Другой подход к экранированию EL, использующий преобразования XSL для предварительной обработки файлов JSP, можно найти здесь:

http://therning.org/niklas/2007/09/preprocessing-jsp-files-to-automatically-escape-el-expressions/

person Brad Parks    schedule 11.07.2012
comment
Привет, Брэд, я ищу вышеупомянутый вариант использования. Не могли бы вы объяснить, как вы можете предотвратить xss в случае вышеуказанного сценария (для каждого из них) - person Samir Vasani; 08.05.2020
comment
Единственное, что я действительно помню сейчас, это использование распознавателя EL - это то, что мы в конечном итоге использовали в нашей компании. В основном он автоматически экранирует все, и если вы действительно не хотите, чтобы что-то экранировалось, вы можете обернуть это <enhance:out escapeXml="false">, как подробно описано в статье. - person Brad Parks; 08.05.2020

Если вы хотите убедиться, что ваш $ оператор не пострадал от взлома XSS, вы можете реализовать ServletContextListener и провести там некоторые проверки.

Полное решение по адресу: http://pukkaone.github.io/2011/01/03/jsp-cross-site-scripting-elresolver.html.

@WebListener
public class EscapeXmlELResolverListener implements ServletContextListener {
    private static final Logger LOG = LoggerFactory.getLogger(EscapeXmlELResolverListener.class);


    @Override
    public void contextInitialized(ServletContextEvent event) {
        LOG.info("EscapeXmlELResolverListener initialized ...");        
        JspFactory.getDefaultFactory()
                .getJspApplicationContext(event.getServletContext())
                .addELResolver(new EscapeXmlELResolver());

    }

    @Override
    public void contextDestroyed(ServletContextEvent event) {
        LOG.info("EscapeXmlELResolverListener destroyed");
    }


    /**
     * {@link ELResolver} which escapes XML in String values.
     */
    public class EscapeXmlELResolver extends ELResolver {

        private ThreadLocal<Boolean> excludeMe = new ThreadLocal<Boolean>() {
            @Override
            protected Boolean initialValue() {
                return Boolean.FALSE;
            }
        };

        @Override
        public Object getValue(ELContext context, Object base, Object property) {

            try {
                    if (excludeMe.get()) {
                        return null;
                    }

                    // This resolver is in the original resolver chain. To prevent
                    // infinite recursion, set a flag to prevent this resolver from
                    // invoking the original resolver chain again when its turn in the
                    // chain comes around.
                    excludeMe.set(Boolean.TRUE);
                    Object value = context.getELResolver().getValue(
                            context, base, property);

                    if (value instanceof String) {
                        value = StringEscapeUtils.escapeHtml4((String) value);
                    }
                    return value;
            } finally {
                excludeMe.remove();
            }
        }

        @Override
        public Class<?> getCommonPropertyType(ELContext context, Object base) {
            return null;
        }

        @Override
        public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base){
            return null;
        }

        @Override
        public Class<?> getType(ELContext context, Object base, Object property) {
            return null;
        }

        @Override
        public boolean isReadOnly(ELContext context, Object base, Object property) {
            return true;
        }

        @Override
        public void setValue(ELContext context, Object base, Object property, Object value){
            throw new UnsupportedOperationException();
        }

    }

}

Опять же: это только охраняет $. См. Также другие ответы.

person Alireza Fattahi    schedule 23.11.2019