Soporte de Spring Security 3.2 CSRF para solicitudes de varias partes

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 lanza un error Acceso denegado que cita 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:

  1. No se declara el MultipartFilter en web.xml .
  2. No se establece el nombre del bean resolutor para MultipartFilter en web.xml .
  3. Utilizando el nombre de filterMultipartResolver resolución predeterminado filterMultipartResolver en webContext.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?

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 la respuesta de @ holmis83 , asegurándose de que se agregue antes de la configuración de Spring Security:

  springMultipartFilter springMultipartFilter org.springframework.web.multipart.support.MultipartFilter   springMultipartFilter /*   springSecurityFilterChain springSecurityFilterChain org.springframework.web.filter.DelegatingFilterProxy   springSecurityFilterChain /* ERROR FORWARD REQUEST  

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

  contextConfigLocation  classpath*:springWebMultipartContext.xml   

springWebMultipartContext.xml

      

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.

  ...  

Después de estos cambios utilizando Apache Commons Multipart Resolver, nuestra aplicación está trabajando hasta ahora en Tomcat, Jetty y Weblogic.

Esta parte:

  multipartFilter /*  

Debiera ser:

  multipartFilter /*  

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.

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 tags 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:

             

Más información: Spring Security – CSRF para solicitudes AJAX y JSON

La mayoría de las respuestas se encuentran en el servidor hace años.

Si necesitas

Pasar tokens CSRF con RestTemplate

Este blog es bastante esclarecedor https://cloudnative.tips/passing-csrf-tokens-with-resttemplate-736b336a6cf6

En Spring Security 5.0.7.RELEASE

https://docs.spring.io/spring-security/site/docs/current/reference/html/csrf.html#csrf-multipart

Hay dos opciones para usar la protección CSRF con multipart / form-data. Cada opción tiene sus compensaciones.

– Colocación de MultipartFilter antes de Spring Security
-Incluir token de CSRF en acción

Para abreviar, la primera opción es más segura, la última es más fácil.

Especificar MultipartFilter antes del filtro Spring Security significa que no hay autorización para invocar el MultipartFilter, lo que significa que cualquiera puede colocar archivos temporales en su servidor. Sin embargo, solo los usuarios autorizados podrán enviar un archivo procesado por su aplicación. En general, este es el enfoque recomendado porque la carga temporal de archivos debería tener un impacto insignificante en la mayoría de los servidores.

Para garantizar que MultipartFilter se especifique antes del filtro Spring Security con la configuración de java, los usuarios pueden anular beforeSpringSecurityFilterChain como se muestra a continuación:

 public class SecurityApplicationInitializer extends AbstractSecurityWebApplicationInitializer { @Override protected void beforeSpringSecurityFilterChain(ServletContext servletContext) { insertFilters(servletContext, new MultipartFilter()); } } 

Para garantizar que MultipartFilter se especifique antes del filtro de Spring Security con configuración XML, los usuarios pueden asegurarse de que el elemento de MultipartFilter se coloque antes de springSecurityFilterChain en web.xml como se muestra a continuación:

  MultipartFilter org.springframework.web.multipart.support.MultipartFilter   springSecurityFilterChain org.springframework.web.filter.DelegatingFilterProxy   MultipartFilter /*   springSecurityFilterChain /*  

Otra opción

Si no se permite que usuarios no autorizados carguen archivos temporales, una alternativa es colocar el MultipartFilter después del filtro de Spring Security e incluir el CSRF como un parámetro de consulta en el atributo de acción del formulario. Un ejemplo con un jsp se muestra a continuación

 

La desventaja de este enfoque es que los parámetros de consulta pueden filtrarse. Más en general, se considera la mejor práctica colocar datos confidenciales dentro del cuerpo o en los encabezados para garantizar que no se filtren.