Skip to main content

Análisis de la vulnerabilidad de omisión de autorización de Spring Security (CVE-2022-31692)

Escrito por
feature spring security auth bypass

16 de diciembre de 2022

0 minutos de lectura

A principios de noviembre se encontró una nueva vulnerabilidad de omisión de autorización en Spring Security 5. Antes de entrar en pánico, analicemos este problema para ver si eres vulnerable. Aunque la vulnerabilidad está clasificada como alta, solo afecta a un conjunto específico de casos de uso. Esto significa que no todo el mundo es vulnerable, como demostraré en un momento. En cualquier caso, se recomienda actualizar a una versión más reciente de Spring Security: la 5.6.9 o una posterior, o la 5.7.5 o una posterior.

Qué es una omisión de autorización

Básicamente, el nombre describe muy bien el problema. Supongamos que tenemos una página web en nuestra aplicación Spring Boot a la que solo deberían poder acceder los usuarios configurados con el rol de administrador. Una omisión de autorización significa que, en ciertos casos de uso, un usuario que no es administrador podría acceder a esa página sin tener ese rol (ni uno superior). Evidentemente, esto no debería ocurrir y puede provocar distintos problemas, como filtraciones de datos y cambios, creación o eliminación de datos sin autorización.

Cómo funciona esta omisión de autorización de Spring Security

Con Spring Security, es posible crear una SecurityFilterChain para definir permisos para endpoints específicos. En las versiones más recientes de Spring, se ve algo así:

@Bean
  public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  http
     .authorizeHttpRequests((authorize) -> authorize
        .antMatchers("/").permitAll()
        .antMatchers("/forward").permitAll()
        .antMatchers("/admin").hasAuthority("ROLE_ADMIN")
        .shouldFilterAllDispatcherTypes(true)
     )
     .httpBasic().and()
     .userDetailsService(userDetailsService());
      return http.build();
  }

En el ejemplo anterior, usé antMatchers para definir los permisos de los patrones de URL, independientemente del método HTTP. Todos pueden acceder a / y /forward. Solo los usuarios con el rol de administrador pueden acceder a la URL /admin.

Hasta aquí, todo bien. Nada complicado y funciona a la perfección. Ahora veamos la implementación del endpoint forward en mi controlador.

@GetMapping("/forward")
public String redirect() {
  return "forward:/admin";
}

En la implementación anterior, el endpoint forward reenvía la solicitud al endpoint admin. Además, agregué todos los tipos de dispatcher a mi configuración de Spring Security, ya que de forma predeterminada solo se contemplan request, async y error. Agregué la siguiente línea en el archivo application.properties de mi aplicación Spring Boot.

spring.security.filter.dispatcher-types = request, error, async, forward, include

Aunque podrías suponer que esto está bien y que los filtros protegen el endpoint admin, resulta que ahora puedo acceder al endpoint admin a través de /forward sin tener el rol de administrador ni iniciar sesión. Aunque agregamos explícitamente forward a nuestra configuración y habilitamos shouldFilterAllDispatcherTypes(), seguimos pudiendo acceder a la página de administración. Al usar forward e incluir todos los tipos de dispatch, es posible omitir la autorización y acceder a un endpoint protegido con un nivel de privilegios superior.

Ventana del navegador en localhost:8080/forward que muestra el texto «Esta es la página de administración»

Esto es posible en Spring Security 5 porque, de forma predeterminada, los filtros no se aplican más de una vez a una solicitud. Por lo tanto, los usuarios deben configurar Spring Security explícitamente para que lo haga. Además, FilterChainProxy tampoco está configurado para ejecutarse con los tipos de dispatcher forward e include.

El problema también se presenta en versiones anteriores de Spring Security 5

Si usas una versión anterior de una aplicación Spring, lo más probable es que también tengas una versión anterior de Spring Security. Una aplicación Spring Boot antigua que mantengo para talleres se basa en la versión 2.2.0-RELEASE, que incluye Spring Security 5.2.0-RELEASE. En estas versiones anteriores, teníamos que configurar la seguridad de otra manera. Debíamos crear una clase de configuración que extendiera la interfaz WebSecurityConfigurerAdapter y sobrescribir el método configure() para implementar filtros similares a los mencionados antes.

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

   @Override
   protected void configure(HttpSecurity http) throws Exception {
       http
               .authorizeRequests()
               .antMatchers("/").permitAll()
               .antMatchers("/forward").permitAll()
               .antMatchers("/admin").hasAuthority("ROLE_ADMIN");
   }
}

En el ejemplo anterior, recreamos una vulnerabilidad similar a la de antes. Básicamente, usamos la misma cadena de filtros donde se produce el problema.

Qué tan grave es este problema en Spring Security

Como siempre, depende de cómo lo uses. Aunque CVE-2022-31692 tiene una puntuación de 9.8 (crítica) según la National Vulnerability Database (NVD), en Snyk le asignamos una puntuación un poco menor: 7.4, lo que la convierte en una vulnerabilidad de gravedad alta.

Aunque la vulnerabilidad es bastante fácil de explotar y está ampliamente disponible, solo afecta realmente a un conjunto específico de casos de uso. Si dependes de los tipos de dispatch forward e include y usas AuthorizationFilters como en los ejemplos anteriores, podrías tener un problema. Si con estos tipos de dispatch llamas a un endpoint que tiene un nivel de privilegios superior y usas AuthorizationFilter para especificar los privilegios, esto puede provocar una omisión de autorización.

Esto significa que deben cumplirse muchos requisitos para que esta vulnerabilidad te afecte. Consulta nuestro aviso sobre este problema de seguridad en Snyk Vulnerability Database para ver la lista completa.

Qué puedes hacer para mitigar este problema de seguridad

La mejor y más sencilla solución es actualizar tu versión de Spring Security a 5.7.5 o una posterior. Si todavía usas la rama 5.6.x, el problema está corregido en 5.6.9. Si trabajas con una aplicación Spring Boot, lo mejor que puedes hacer es actualizar a la versión 2.7.6 de Spring Boot (o una posterior), que incluye automáticamente la versión correcta de Spring Security si la estás usando.

Si no puedes actualizar, puedes cambiar la definición del filtro de authorizeHttpRequests().shouldFilterAllDispatcherTypes(true) a .authorizeRequests().filterSecurityInterceptorOncePerRequest(false). Esto garantizará que el tipo de dispatch forward se filtre como corresponde.

Al reescribir el primer ejemplo de esta publicación, la solución queda así:

@Configuration
public class SecurityConfig {

   @Bean
   public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
       http
               .authorizeRequests()
               .filterSecurityInterceptorOncePerRequest(false)
               .antMatchers("/").permitAll()
               .antMatchers("/forward").permitAll()
               .antMatchers("/admin").hasAuthority("ROLE_ADMIN")
               .and()
               .httpBasic().and()
               .userDetailsService(userDetailsService());
       return http.build();
   }
}

Del mismo modo, podemos agregar .filterSecurityInterceptorOncePerRequest(false) a la forma antigua de definir un filtro, en la que debíamos extender WebSecurityConfigurerAdapter.

Actualiza tus dependencias para mantenerte seguro

Esto demuestra una vez más que es fundamental mantener tus dependencias actualizadas. Cuando un problema de seguridad como este afecta a tu aplicación y tus dependencias están desactualizadas, necesitarás mucho más esfuerzo para mitigar la situación. Al usar funciones de análisis SCA, como las de Snyk Open Source, tendrás esta información y recomendaciones para corregir el problema en cuanto estén disponibles. Puedes usar Snyk gratis y, específicamente para el desarrollo en Java, escribí un artículo sencillo sobre cómo empezar.

Empieza con los desafíos de Capture the Flag

Aprende a resolver desafíos de captura la bandera con nuestro taller virtual introductorio a pedido.