Webflux multipart / form-data, csrf включен, с загрузкой файла и без нее, получая неверный токен CSRF - PullRequest
0 голосов
/ 29 августа 2018

С отключенным csrf я могу загрузить файл, однако мне нужно, чтобы он был включен. Проблема возникает только в том случае, если тип формы представляет собой multipart / form-data, а именно «Недопустимый токен CSRF» с 403.

Обычно, когда я устанавливаю enctype как multipart / form-data даже для формы без загрузки файла, я получаю ту же ошибку.

Использование этой зависимости:

<dependency>
  <groupId>org.synchronoss.cloud</groupId>
  <artifactId>nio-multipart-parser</artifactId>
  <version>...</version>
</dependency>

Попытался включить скрытый ввод csrf в форму, а также попытался добавить его к URL, но та же ошибка

    <form  method="post" th:action="${'/add/' + id + '/documents?' + _csrf.headerName + '=' + _csrf.token}"  enctype="multipart/form-data">
        <input type="file" name="documents" multiple="multiple">
        <input  type="hidden"
                th:name="${_csrf.headerName}"
                th:value="${_csrf.token}" />
        <input type="hidden" name="_csrf" th:value="${_csrf.token}">
        <button class="btn btn-success btn-l">Upload</button>
    </form>

Получите такой совет от контроллера для впрыска csrf

@ControllerAdvice
public class SecurityAdvice {@ModelAttribute("_csrf")Mono<CsrfToken> csrfToken(final ServerWebExchange exchange) {
    final Mono<CsrfToken> csrfToken = exchange.getAttributeOrDefault(org.springframework.security.web.server.csrf.CsrfToken.class.getName(), Mono.empty());
    return csrfToken;
}

В безопасности у меня есть следующий компонент:

 @Bean
    public ServerCsrfTokenRepository csrfTokenRepository() {
        WebSessionServerCsrfTokenRepository repository =
                new WebSessionServerCsrfTokenRepository();
        repository.setHeaderName("X-CSRF-TK");

        return repository;
    }

и использовать его следующим образом в моей SecurityWebFilterChain:

.and().csrf().csrfTokenRepository(csrfTokenRepository())

UPDATE:

Отключение csrf для нескольких URL тоже будет достаточно. Нашел несколько примеров, но все они для версии на основе сервлетов. https://sdqali.in/blog/2016/07/20/csrf-protection-with-spring-security-and-angular-js/

1 Ответ

0 голосов
/ 05 сентября 2018

Ознакомьтесь с официальной рекомендацией Spring Security: https://docs.spring.io/spring-security/site/docs/current/reference/html/csrf.html#csrf-multipart

Есть два основных способа сделать это: (1) поместить MultipartFilter перед фильтром Spring Security и (2) включить маркер CSRF в действие формы, как вы это делаете. Первый вариант является рекомендуемым:

Первый вариант - убедиться, что указан MultipartFilter. перед фильтром Spring Security. Указание MultipartFilter перед фильтром Spring Security означает, что нет авторизации для вызова MultipartFilter, что означает, что каждый может разместить временные файлы на вашем сервере. Тем не менее, только авторизованные пользователи будут возможность отправить файл, который обрабатывается вашим приложением. В Вообще, это рекомендуемый подход, потому что временный файл загрузка должна иметь незначительное влияние на большинство серверов.

Чтобы убедиться, что MultipartFilter указан перед фильтром Spring Security с конфигурацией Java, пользователи могут переопределить beforeSpringSecurityFilterChain, как показано ниже:

public class SecurityApplicationInitializer extends AbstractSecurityWebApplicationInitializer {

    @Override
    protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
        insertFilters(servletContext, new MultipartFilter());
    }
}

Чтобы убедиться, что MultipartFilter указан перед фильтром Spring Security с конфигурацией XML, пользователи могут убедиться, что элемент MultipartFilter размещен перед springSecurityFilterChain в файле web.xml, как показано ниже:

<filter>
    <filter-name>MultipartFilter</filter-name>
    <filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
</filter>
<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>MultipartFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Обратите внимание, что если вы все еще хотите использовать действие формы, параметры запроса могут быть пропущены. Попробуйте изменить "headerName" на "имя_параметра":

<form action="./upload?${_csrf.parameterName}=${_csrf.token}" method="post" enctype="multipart/form-data">

РЕДАКТИРОВАТЬ : Если вы не можете переключиться на контейнер на основе сервлета (например, Jetty или Tomcat), и рекомендация по действию формы не работает, существует недавний поток Stack Overflow обсуждает эту проблему.

Один из разработчиков сообщил, что может решить проблему с помощью AJAX:

Я решил эту проблему следующим образом:

  • отправка файла из нескольких частей с использованием vanilla javascript, как в Руководство Mozilla
  • добавление токена _csrf в заголовок HTML в мета теги, как в руководстве Spring для отправки токена CSRF с помощью Ajax
  • вместо использования jquery, добавив его непосредственно к объекту XHR

var csrfToken = $("meta[name='_csrf']").attr("content"); var csrfHeader = $("meta[name='_csrf_header']").attr("content"); XHR.setRequestHeader(csrfHeader, csrfToken); XHR.setRequestHeader('Content-Type','multipart/form-data; boundary=' + boundary); XHR.send(data);

Тот же разработчик сообщил об этой проблеме в Spring , но пока не получил никакого внимания.

...