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

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

10 ответов

Решение

XSS можно предотвратить в JSP с помощью JSTL <c:out> тег или fn:escapeXml() Функция EL при (пере) отображении контролируемого пользователем ввода. Это включает в себя параметры запроса, заголовки, куки, 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 (Servlet), поскольку они там безвредны. Некоторые могут решить избегать их во время обработки запроса (как вы делаете в Servlet или Filter) вместо обработки ответа (как вы делаете в JSP), но таким образом вы можете рискнуть, что данные излишне получат двойное экранирование (например, & становится &amp;amp; вместо &amp; и в конечном итоге конечный пользователь увидит &amp; или что данные, хранящиеся в БД, становятся непереносимыми (например, при экспорте данных в JSON, CSV, XLS, PDF и т. д., которые вообще не требуют экранирования HTML). Вы также потеряете социальный контроль, потому что вы больше не знаете, что на самом деле заполнил пользователь. Вы, как администратор сайта, действительно хотели бы знать, какие пользователи / IP-адреса пытаются выполнить XSS, чтобы вы могли легко отслеживать их и принять меры соответственно. Экранирование во время обработки запроса следует использовать только в качестве последнего средства, когда вам действительно необходимо в кратчайшие сроки исправить крушение поезда плохо разработанного устаревшего веб-приложения. Тем не менее, в конечном итоге вам следует переписать файлы JSP, чтобы они стали безопасными для XSS.

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

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


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

Мне очень повезло с OWASP Anti-Samy и консультантом AspectJ по всем моим Spring Controllers, который блокирует доступ 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 из этого поста

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

Вопрос о том, как предотвратить xss, задавался несколько раз. Вы найдете много информации в Stackru. Кроме того, на веб-сайте OWASP есть шпаргалка по профилактике XSS, которую вы должны пройти.

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

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

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

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

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

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

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

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

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

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

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

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

Если вы хотите автоматически экранировать все переменные 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

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

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

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

Если вы хотите убедиться, что ваш $ оператор не страдает от взлома 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();
        }

    }

}

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

      <%@ page import="org.apache.commons.lang.StringEscapeUtils" %>
String str=request.getParameter("urlParam");
String safeOuput = StringEscapeUtils.escapeXml(str);
Другие вопросы по тегам