Broken Function Level Authorization

How to Fix Broken Function Level Authorization in Spring Boot March 2026 [CVE-2018-1196]

[Fixed month year] Updated CVE-2018-1196

Overview

These real-world issues highlight how deployment tooling can undermine function-level authorization in Spring Boot. CVE-2018-1196 describes a symlink vulnerability in the embedded launch script used when Spring Boot applications are installed as Linux services. If the application is started by the privileged run_user and that user has shell access, an attacker could leverage a symlink to overwrite and take ownership of arbitrary files on the same system. The vulnerability is specific to certain release lines (the embedded script in Spring Boot 1.5.9 and earlier and 2.0.0.M1 through 2.0.0.M7) and requires the app to be deployed as a service with shell-enabled run_user. This creates a dangerous boundary where OS-level access can bypass or undermine in-app authorization controls, effectively turning system privileges into a way to perform privileged actions in the app. Not all Spring Boot deployments are affected; non-service deployments or those not using the embedded launcher are not susceptible. The CVE references demonstrate how insecure deployment tooling can enable privilege escalation that bypasses intended authorization controls in the application layer.

Affected Versions

Spring Boot 1.5.x up to 1.5.9; and Spring Boot 2.0.0.M1 through 2.0.0.M7

Code Fix Example

Spring Boot API Security Remediation
Vulnerable:
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/priv")
public class PrivController {
  @GetMapping("/action")
  public ResponseEntity<String> perform(@RequestHeader(value = "X-ROLE", required = false) String role) {
    // Vulnerable pattern: trusts client-provided role header for authorization
    if ("ADMIN".equals(role)) {
      return new ResponseEntity<>("privileged action performed", HttpStatus.OK);
    }
    return new ResponseEntity<>("Forbidden", HttpStatus.FORBIDDEN);
  }
}

Fixed (Spring Security):
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestController;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http
      .csrf().disable()
      .authorizeRequests()
        .antMatchers("/priv/**").hasRole("ADMIN")
        .anyRequest().authenticated()
      .and()
      .httpBasic();
  }

  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication()
      .withUser("admin").password("{noop}secret").roles("ADMIN");
  }
}

@RestController
@RequestMapping("/priv")
public class PrivControllerFixed {
  @PreAuthorize("hasRole('ADMIN')")
  @GetMapping("/action")
  public String perform() {
    return "privileged action performed";
  }
}

CVE References

Choose which optional cookies to allow. You can change this any time.