Security

OAuth2 Feature Matrix. All OAuth 2.0 Client and Resource Server features implemented by Spring. The matrix may be used to determine which project to use based on your OAuth 2.0 Client and Resource Server requirements.

OAuth2 Login Spring Boot samples

Spring Security components

Features

  • Authentication. It is how we verify the identity of who is trying to access a particular resource. A common way to authenticate users is by requiring the user to enter a username and password. Once authentication is performed we know the identity and can perform authorization.

    • Password storage.

      • PasswordEncoder is used to perform one way password transformation to store it securely (DelegatingPasswordEncoder)

  • Protection against Exploits

    • Cross Site Request Forgery (CSRF). The reason that a CSRF attack is possible is that the HTTP request from the victim’s website and the request from the attacker’s website are exactly the same. This means there is no way to reject requests coming from the evil website and allow requests coming from the bank’s website. To protect against CSRF attacks we need to ensure there is something in the request that the evil site is unable to provide so we can differentiate the two requests.

      • When should you use CSRF protection? Our recommendation is to use CSRF protection for any request that could be processed by a browser by normal users. If you are only creating a service that is used by non-browser clients, you will likely want to disable CSRF protection.

    • Security HTTP Response Headers.

      • Cache-Control: no-cache, no-store, max-age=0, must-revalidate
        Pragma: no-cache
        Expires: 0
        X-Content-Type-Options: nosniff
        Strict-Transport-Security: max-age=31536000 ; includeSubDomains
        X-Frame-Options: DENY
        X-XSS-Protection: 1; mode=block
      • Default Security HTTP Response Headers

Servlet security. Big Picture

  • Spring provides Filter implementation named DelegatingFilterProxy which is a bridge for Servlet container lifecycle and Spring ApplicationContext.

  • DelegatingFilterProxy can be registered via standard Servlet container mechanism, but delegate all the work to a Spring Bean that implements Filter

  • public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
        // Lazily get Filter that was registered as a Spring Bean
        // For the example in DelegatingFilterProxy delegate is an instance of Bean Filter0
        Filter delegate = getFilterBeanFromApplicationContext(someBeanName);
        // delegate work to the Spring Bean
        delegate.doFilter(request, response);
    }
  • FilterChainProxy is a special Filter provided by Spring Security that allows delegating to many Filter instances through SecurityFilterChain.

  • FilterChainProxy is a starting point for all of Spring Security Servlet support.

    • 💡 FilterChainProxy is a great place to start debugging

    • FilterChainProxy clears SecurityContext to avoid memory leaks

    • FilterChainProxy can be used to determine which SecurityFilterChain should be used.

if (URL == "/api/messages") only SecurityFilterChain0 will be invoked;
if (URL == "/messages") SecurityFilterChainN will be invoked;

  1. ExceptionTranslationFilter invokes FilterChain.doFilter(request, response) to invoke the rest of application

  2. if (!user.authenticated() || AuthenticationException) {
        startAuthentication();
    }
    
    void startAuthentication() {
        clear SecurityContextHolder;
        requestCache.save(HttpServletRequest);
        AuthenticationEntryPoint.requestCredentialsFromClient();
        // e.g. login page OR WWW-Authenticate header
    }

    3. If (AccessDeniedException) => AccessDeniedHandler.handleAccessDenied();

Authentication

SecurityContextHolder

This is a 💙 heart of Spring Security authentication model.

SecurityContext context = SecurityContextHolder.createEmptyContext(); 
Authentication authentication =
    new TestingAuthenticationToken("username", "password", "ROLE_USER"); 
context.setAuthentication(authentication);

SecurityContextHolder.setContext(context); 
// BAD! SecurityContextHolder.getContext().setAuthentication(authentication) to 
// avoid race conditions

SecurityContextHolder uses ThreadLocal to store details.

ProviderManager

ProviderManager is the most commonly user implementation of AuthenticationManager.

  • each AuthenticationProvider knows how to perform a specific type of authentication.

    • e.g. one is able to validate username/password

    • another is able to validate SAML assertion

In fact, multiple ProviderManager instances might share the same parent AuthenticationManager. Common for scenarios where there are multiple SecurityFilterChain instances that have some authentication in common.

By default ProviderManager will clear any sensitive credentials info from Authentication object which is returned by a successful authentication request.

AuthenticationEntryPoint

Used to send an HTTP response that requests credentials from a client.

AbstractAuthenticationProcessingFilter

  1. Based on subclass of AbstractAuthenticationProcessingFilter certain type of Authentication is created. E.g. for UserPasswordAuthenticationFilter UserPasswordAuthenticationToken is created from username and password from HttpServletRequest.

  2. Authentication is passed to AuthenticationManager.

  3. if (authentication fails) {
        clear SecurityContextHolder;
        RememberMeServices.loginFail
        invoke AuthenticationFailureHandler;
    }    
  4. if (authentication success) {
        SessionAuthenticationStrategy is notified of new login;
        SecurityContextHolder saves Authentication;
        RememberMeServices.loginSuccess;
        ApplicationEventPublisher.publish(InteractiveAuthenticationSuccessEvent);
        invoke AuthenticationSuccessHandler;
    }

Username/Password Authentication

  • Reading username and password from HttpServletRequest

    • FormLogin

    • Basic Authentication

    • Digest Authentication

  • Storage mechanisms

    • Simple (in-memory)

    • Relational DB (JDBC Authentication)

    • Custom data stores with UserDetailService

    • LDAP storage with LDAP Authentication

When the username and password submitted:

protected void configure(HttpSecurity http) {
    http
        // ...
        .formLogin(withDefaults());
}
// custom login form configuration
protected void configure(HttpSecurity http) throws Exception {
    http
        // ...
        .formLogin(form -> form
            .loginPage("/login") // src/main/resources/templates/login.html
            .permitAll()
        );
}

When a client receives WWW-Authenticate header it knows it should retry with a username and password.

// minimum explicit configuration
protected void configure(HttpSecurity http) {
    http
        // ...
        .httpBasic(withDefaults());
}

Digest Authentication

💡 🔥 ⚠️ You should not use Digest Authentication in modern applications because it is not considered secure. The most obvious problem is that you must store your passwords in plaintext, encrypted, or an MD5 format. All of these storage formats are considered insecure. Instead, you should store credentials using a one way adaptive password hash (i.e. bCrypt, PBKDF2, SCrypt, etc) which is not supported by Digest Authentication.

InMemoryUserDetailsManager implements UserDetailsService.

@Bean
public UserDetailsService users() {
    UserDetails user = User.builder()
        .username("user")
        .password("{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW")
        .roles("USER")
        .build();
    UserDetails admin = User.builder()
        .username("admin")
        .password("{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW")
        .roles("USER", "ADMIN")
        .build();
    return new InMemoryUserDetailsManager(user, admin);
}

JdbcUserDetailsManager extends JdbcDaoImpl (which implements UserDetailsService)

ℹ️ The default schema is also exposed as a classpath resource named org/springframework/security/core/userdetails/jdbc/users.ddl.

There is a support for groups.

// configure DataSource (here it is embedded)
@Bean
DataSource dataSource() {
    return new EmbeddedDatabaseBuilder()
        .setType(H2)
        .addScript("classpath:org/springframework/security/core/userdetails/jdbc/users.ddl")
        .build();
}
// configure JdbcUserDetailsManager
@Bean
UserDetailsManager users(DataSource dataSource) {
    UserDetails user = User.builder()
        .username("user")
        .password("{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW")
        .roles("USER")
        .build();
    UserDetails admin = User.builder()
        .username("admin")
        .password("{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW")
        .roles("USER", "ADMIN")
        .build();
    JdbcUserDetailsManager users = new JdbcUserDetailsManager(dataSource);
    users.createUser(user);
    users.createUser(admin);
}

UserDetails and UserDetailsService

DaoAuthenticationProvider uses UserDetailsService to retrieve UserDetails. DaoAuthenticationProvider validates UserDetails and returns Authentication with proper principal.

Did not have a look

Authorization

  • FilterInvocationSecurityMetadataSource

    • by default ExpressionBasedFilterInvocationSecurityMetadataSource is used

  • AccessDecisionManager

  • How is it glued to Filters => AbstractSecurityInterceptor. Subclasses

    • FilterSecurityInterceptor

Protecting common attacks

Cache control

This is about cache control header. Cache-Control.

HTTPS

This is about man-in-the-middle attack. HsTsHeaderWriter

XSS (Cross site scripting)

Header which helps to protect: X-XSS-Protection: 1; mode=block

  • mode block means block the rendering

Content sniffing

X-Content-Type-Options: nosniff

JSR 250 annotations (Method security)

  • @RolesAllowed("ROLE_somerole")

    • ⚠️ Do not forget to prefix roles with ROLE_

  • @Secured("ROLE_somerole")

  • @PreAuthorize("hasRole('ADMIN')")

    • check happens before method is invoked

    • Spring expression language is used

  • @PostAuthorize("@authz.check(returnObject, principal?.user) ")

    • @Service("authz")
      class AuthService {
          public boolean check(Message msg, User user) {
              return // check some conditions for authorization;
              // Message and User can vary as input params
          }
      }

OAuth2

see CommonOAuth2Provider.java ClientRegistrationRepository.java OAuth2AuthorizationRequestRedirectFilter.java OAuth2LoginAuthenticationFilter.java OAuth2LoginAuthenticationProvider.java

Last updated