Как настроить Spring Security для обработки токена CSRF в запросе multipart/form-data (POST)?

98
17

сценарий

    Одностраничное (AngluarJS) веб-приложение с использованием HTTP API для взаимодействия клиент-сервер Spring Security настроена на использование защиты CSRF (через XML) Тонер CSRF обычно отправляется в заголовке запроса (отлично работает) Приложение должно поддерживать загрузку файлов в IE9

проблема

Загрузка файла происходит с помощью запроса POST с использованием multipart/form-data. Обычно это делается с использованием клиентской AJAX, но IE9 не поддерживает FileAPI (http://www.w3.org/TR/FileAPI/).

Обход для IE9 заключается в создании формы в скрытом iframe и отправке формы. Ток CSRF добавляется в тело запроса, добавляя его как ввод формы - причина в том, что я не могу манипулировать заголовками запросов, чтобы добавить заголовок CSRF до отправки формы.

Spring Security org.springframework.security.web.csrf.CsrfFilter сначала пытается получить токен CSRF из заголовков, а если не найден, то пытается получить его из параметров (через HttpServletRequest.getParameter())

Это не работает для многопрофильного запроса с токеном CSRF в теле - getParameter() всегда возвращает null.

(В стороне, этот вызов getParameter() также читает запрос InputStream до конца, поэтому мы вынуждены обернуть запрос до того, как он попадет в CsrfFilter, чтобы запрос InputStream был "кэширован")

Я хочу создать CsrfFilter, который вызывает getPart(), но не может этого сделать, все еще используя ярлыки + чистые элементы пространства имен Spring Security.

Причина в том, что в конфигурации нет места для включения настраиваемого фильтра CSRF - и CsrfConfigurer жестко закодирован для использования org.springframework.security.web.csrf.CsrfFilter, поэтому нельзя вводить его.

Я могу добавить код к переопределенному методу getParameter() моего класса оболочки запроса, чтобы также попытаться проанализировать параметр из многостраничного запроса - но на самом деле это довольно сложно, чтобы получить право, и предпочел бы избежать таких расходов на обслуживание.

TL; DR

    Мы не можем добавить токен CSRF для запроса заголовков, нужно добавить в тело запроса Запрос - это multipart/form-data Фильтр CSRF безопасности Spring не настраивается для синтаксического анализа токена CSRF из многопрофильного запроса

Любая помощь - любые предложения по исправлению на стороне клиента или на стороне сервера - приветствуются!

ТИА

спросил(а) 2015-02-07T00:55:00+03:00 5 лет, 5 месяцев назад
1
Решение
97

Вы должны прочитать раздел в ссылке, в котором рассматриваются запросы CSRF и Multipart. У вас есть два варианта:

Каждый из них имеет свои плюсы и минусы, описанные в ссылке.

В конечном счете, если вы хотите предоставить собственный фильтр, вы можете сделать это, используя элемент XML, который просто ссылается на Spring Bean, который реализует Filter. Например:

<http ...>
...
<custom-filter ref="customCsrfFilter" position="CSRF_FILTER"/>
</http>

ответил(а) 2015-02-07T01:12:00+03:00 5 лет, 5 месяцев назад
39

Я реализовал решение, которое, кажется, работает сейчас, но есть что-то в этом, мне действительно не нравится...

Использование Spring Security <csrf>

Я добавил специальный фильтр перед фильтром CSRF:

<security:custom-filter before="CSRF_FILTER" ref="csrfRequestWrapperFilter"/>

Я ввел тот же RequestMatcher в CsrfRequestWrapperFilter, что и обрабатывать только запросы, которые будут проверяться для токена CSRF Фильтр обертывает/украшает HttpServletRequest в CsrfProtectedHttpServletRequest и продолжает цепочку (далее CsrfFilter) CsrfProtectedHttpServletRequest расширяет HttpServletRequestWrapper и переопределяет метод getParameter (String name). Что-то вроде:

... copy Request InputStream для члена OutputStream...


    if(this.getContentType() != null && requestBody != null) {
if(this.getContentType().contains(MediaType.APPLICATION_FORM_URLENCODED_VALUE) && requestBody.contains(parameterName+"="))
return getFormEncodedParameter(requestBody, parameterName);
else if(this.getContentType().contains(MediaType.MULTIPART_FORM_DATA_VALUE) && requestBody.contains("name="+"\""+ parameterName +"\"")) {
return getMultipartParameter(requestBody, parameterName);
}
}
}

Оба эти метода выполняют синтаксический анализ строк... что часть, которую мне действительно не нравится. Это вовсе не связано с основной бизнес-логикой, и я не уверен на 100% в реализации, хотя он работает, чтобы получить параметр _csrf из _csrf запроса.

ответил(а) 2015-02-07T02:19:00+03:00 5 лет, 4 месяца назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема