disable - Soporte de Spring Security 3.2 CSRF para solicitudes de varias partes
spring security csrf() disable() (3)
Hemos estado usando Spring Security con nuestra aplicación durante algunos años. La semana pasada actualizamos Spring Security de la versión 3.1.4 a 3.2.0. La actualización fue bien y no encontramos ningún error publicar la actualización.
Al revisar la documentación de Spring Security 3.2.0 encontramos las características recientemente agregadas alrededor de la protección CSRF y los encabezados de seguridad. Seguimos las instrucciones en la documentación de Spring Security 3.2.0 para habilitar la protección CSRF para nuestros recursos protegidos. Funciona bien para formularios regulares, pero no funciona para formularios de varias partes en nuestra aplicación. Al enviar el formulario, CsrfFilter
arroja un error de Acceso denegado al citar la ausencia de un token CSRF en la solicitud (determinado a través de los registros de DEPURACIÓN). Hemos intentado utilizar la primera opción sugerida en la documentación de Spring Security para hacer que la protección CSRF funcione con formularios multiparte. No queremos utilizar la segunda opción sugerida, ya que filtra los tokens de CSRF a través de las URL y presenta un riesgo de seguridad.
La parte relevante de nuestra configuración basada en la documentación está disponible como Gist en Github. Estamos usando Spring versión 4.0.0.
Tenga en cuenta que ya hemos probado las siguientes variaciones sin éxito:
- No se declara el
MultipartFilter
enweb.xml
. - No se establece el nombre del bean resolutor para
MultipartFilter
enweb.xml
. - Utilizando el nombre de
filterMultipartResolver
resolución predeterminadofilterMultipartResolver
enwebContext.xml
.
ACTUALIZACIÓN: He confirmado que el comportamiento documentado no funciona incluso con una aplicación de muestra de página única. ¿Alguien puede confirmar que el comportamiento documentado funciona como se espera? ¿Hay alguna aplicación de trabajo de ejemplo que se pueda usar?
Esta parte:
<filter-mapping>
<filter-name>multipartFilter</filter-name>
<servlet-name>/*</servlet-name>
</filter-mapping>
Debiera ser:
<filter-mapping>
<filter-name>multipartFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Es un error en la documentación de Spring Security 3.2.0. El error ha sido reportado y será reparado en la próxima versión.
Pude resolver esto con la ayuda del equipo de Spring Security. He actualizado el Gist para reflejar una configuración funcional. Tenía que seguir los pasos que figuran a continuación para que todo funcione como se esperaba.
1. Paso común
Agregue un MultipartFilter
a web.xml
como se describe en @ holmis83 en la respuesta anterior, asegurándose de que se agrega antes de la configuración de Spring Security:
<filter>
<display-name>springMultipartFilter</display-name>
<filter-name>springMultipartFilter</filter-name>
<filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>springMultipartFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<display-name>springSecurityFilterChain</display-name>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>ERROR</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
2.1. Usando Apache Commons Multipart Resolver
Asegúrese de que haya un bean filterMultipartResolver
Resolver de Apache Commons llamado filterMultipartResolver
en el contexto de la aplicación raíz de Spring . Voy a enfatizar esto de nuevo, asegúrese de que Multipart Resolver esté declarado en el Contexto de Spring raíz (generalmente llamado applicationContext.xml ). Por ejemplo,
web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:springWebMultipartContext.xml
</param-value>
</context-param>
springWebMultipartContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="filterMultipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="100000000" />
</bean>
</beans>
Asegúrese de que el bean se llame filterMultipartResolver ya que MultipartFilter
no ha recogido ningún otro nombre de bean configurado en web.xml
. Mi configuración inicial no funcionaba porque este bean se llamó multipartResolver . Incluso intenté pasar el nombre del bean a MultipartFilter
usando web.xml
init-param
pero tampoco funcionó.
2.2. Uso del soporte Tomcat Multipart
Tomcat 7.0+ tiene soporte multiparte integrado, pero tiene que estar explícitamente habilitado. Cambie el archivo global context.xml
Tomcat de la siguiente manera o incluya un archivo local context.xml
en su archivo WAR para que este soporte funcione sin realizar ningún otro cambio en su aplicación.
<Context allowCasualMultipartParsing="true">
...
</Context>
Después de estos cambios utilizando Apache Commons Multipart Resolver, nuestra aplicación está trabajando hasta ahora en Tomcat, Jetty y Weblogic.
Después de luchar un poco con este tema, encontré una solución mucho más sencilla al usar el Encabezado de solicitud definido en Spring Security en lugar de intentar incorporar el token CSRF como parte del contenido multiparte.
Esta es una forma sencilla de configurar el encabezado usando una biblioteca AJAX para subir archivos en mi jsp:
var uploader = new AjaxUpload({
url: ''/file/upload'',
name: ''uploadfile'',
multipart: true,
customHeaders: { ''${_csrf.headerName}'': ''${_csrf.token}'' },
...
onComplete: function(filename, response) {
...
},
onError: function( filename, type, status, response ) {
...
}
});
Que a su vez envió la solicitud multiparte con encabezado:
X-CSRF-TOKEN: abcdef01-2345-6789-abcd-ef0123456789
Sus recomendaciones para incrustación en las etiquetas <meta />
en el encabezado también funcionarían bien deteniendo la solicitud al enviar, agregando el encabezado a través de javascript, y luego terminando de enviar:
<html>
<head>
<meta name="_csrf" content="${_csrf.token}"/>
<!-- default header name is X-CSRF-TOKEN -->
<meta name="_csrf_header" content="${_csrf.headerName}"/>
<!-- ... -->
</head>
<body>
<!-- ... -->
<script>
var token = $("meta[name=''_csrf'']").attr("content");
var header = $("meta[name=''_csrf_header'']").attr("content");
// Do whatever with values
</script>
</body>
</html>
Más información: Spring Security - CSRF para solicitudes AJAX y JSON