Spring Security BASIC auth - matching password hash - spring

When doing BASIC authentication with Spring Security I want to match password hash and not password itself. For the sake of storing hash and not the password server-side.
I have the following code:
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
AppConfig appConfig;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/v2/**")
.hasAuthority(MY_AUTHORITY).anyRequest().authenticated()
.and()
.httpBasic();
}
#Autowired
public void configureSecurityManager(AuthenticationManagerBuilder authManagerBuilder) throws Exception {
new InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder>()
.withUser(appConfig.getCredentials().getUserName())
.password(appConfig.getCredentials().getPassword())
.authorities(MY_AUTHORITY)
.and()
.configure(authManagerBuilder);
}
}
I've found some examples with BCrypt, however they're dealing with hashing the password and not incorporating hash comparison into BASIC auth scheme.
Or am I getting this wrong all the way and client should send hash and not the password to the server?

Here you have an example: http://www.devglan.com/spring-security/spring-boot-security-password-encoding-bcrypt-encoder
Once the password encoder is defined, while comparing password coming from the request with the DB password, spring will by default consider password in the DB is bcrypt encoded.
Also I recommend you the following link: https://security.stackexchange.com/questions/64631/is-it-safe-to-send-clear-usernames-passwords-on-a-https-connection-to-authentica

Related

hasRole() and denyAll() method don't restrict access to resources

I'm developing Spring Boot and Spring Security web application with authorization and resource servers enabled. I have defined a set of users with roles assigned to them and trying to implement roles based access to REST endpoints. I was able to implement token based access to endpoints, but can't restrict access to end users, that would be based on their roles.
I have done two endpoints: /rest/products/list and /rest/products/add and trying to restrict access to /rest/products/add endpoint with the user that is of ADMIN role.
My WebSecurityConfigurerAdapter is as follows:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private PasswordEncoder passwordEncoder;
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.passwordEncoder(passwordEncoder)
.withUser("user1")
.password(passwordEncoder.encode("user1Pass"))
.roles("USER")
.and()
.withUser("user2")
.password(passwordEncoder.encode("user2Pass"))
.roles("USER")
.and()
.withUser("admin")
.password(passwordEncoder.encode("adminPass"))
.roles("ADMIN");
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/rest/products/add").hasAnyRole("ADMIN")
.antMatchers("/rest/products/list").denyAll();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
Therefore, resource /rest/products/add should be accessible to admin / adminPass user only as far as that user has ADMIN role. But if to try to it with user1 / user1Pass, it is still accessible:
Get access token for user1 postman screen
Accessing ADMIN only related endpoint with user1 Postman screen
Also I added (in the testing purpose) in the configuration method the following rule .antMatchers("/products/list").denyAll(); Here is indicated that /products/list shouldn't be accessible to any user. But it still keeps on responding (provided access correct token).
In the similar question here How to fix role in Spring Security? the order of matchers should be from the more specific to the less. But in my case there are two matchers and no matchers that can overlap them.
I'm using Spring Boot with spring-boot-starter-security plugin version 2.5.2.
What additional configuration should be done to make .hasRole("ADMIN") and .denyAll() work as expected?
Finally was able to find the solution with the following:
Here there is an example of ResourceServerConfigurerAdapter class. From this and from your comment, dur, I realized that I confused ResourceServerConfigurerAdapter and WebSecurityConfigurerAdapter, trying to define access restriction matchers in WebSecurityConfigurerAdapter. I changed resource server configuration in the following way:
Method that was in WebSecurityConfigurerAdapter
#Override
public void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/rest/products/add").hasAnyRole("ADMIN")
.antMatchers("/rest/products/list").denyAll();
}
was moved to
#Configuration
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/rest/products/add").hasAnyRole("ADMIN")
.antMatchers("/rest/products/list").denyAll();
}
}
Now restrictions defined by matchers above are working as expected.

Why do I get a 401 error when I enable csrf?

So I am working on my first full-stack application (spring boot rest API and Vue.js frontend) and I came across a problem by using sonarqube.
My sonarqube gives the following warning:
Make sure disabling Spring Security's CSRF protection is safe here.
and it is coming from this file:
#Configuration
#AllArgsConstructor
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class
WebSecurityConfig extends WebSecurityConfigurerAdapter {//provides security for endpoints
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
#Autowired
private UserDetailsService jwtUserDetailsService;
#Autowired
private JwtRequestFilter jwtRequestFilter;
private final AccountService accountService;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// configure AuthenticationManager so that it knows from where to load
// user for matching credentials
// Use BCryptPasswordEncoder
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()/*.disable()*/.and()//So we can send post requests without being rejected(if we using form based indication we want to enable this)
.authorizeRequests()
.antMatchers("/login", "/authenticate","/register", "/register/**")
.permitAll()//any request that goes trough that end point we want to allow.
.anyRequest()
.authenticated().and().exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
http.cors();
http.logout().permitAll();
http.logout().logoutSuccessHandler((new HttpStatusReturningLogoutSuccessHandler(HttpStatus.OK)));
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(daoAuthenticationProvider());
}
#Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider =
new DaoAuthenticationProvider();
provider.setPasswordEncoder(bCryptPasswordEncoder);
provider.setUserDetailsService(jwtUserDetailsService);
return provider;
}
}
More specifically this piece of code:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()/*.disable()*/.and()//So we can send post requests without being rejected(if we using form based indication we want to enable this)
.authorizeRequests()
.antMatchers("/login", "/authenticate","/register", "/register/**")
.permitAll()//any request that goes trough that end point we want to allow.
.anyRequest()
.authenticated().and().exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
http.cors();
http.logout().permitAll();
http.logout().logoutSuccessHandler((new HttpStatusReturningLogoutSuccessHandler(HttpStatus.OK)));
When I remove the first .and() and use disable (which is commented out now) my program works, but I want to find a solution where I can .csrf() let be enabled (I know it is standard enabled) and where my login stops giving me a 401 error.
Thanks in advance!
Apparently, you are using JWTs for authenticating requests. This typically does not involve cookies (tokens are usually sent as request headers). If this is the case for you (JWT is received in a header) you can disregard the Sonarqube warning, you don't need CSRF protection.
The reason for this is CSRF is an attack where the attacker exploits the existing session of a victim user, when the victim visits a malicious website. This is based on cookies being sent with a request by the browser only depend on the destination, and not the origin (ie. a javascript on attacker.com can make a request to victim.com, and a user's cookies for victim.com will be sent automatically). If request headers are used to transmit a token, that cannot be accessed by an attacker on their malicious domain.
If you still wanted to make it work (because for example your JWTs are indeed received from a cookie), you would have to send the correct CSRF token from your frontend (VueJS) with every request that's not a get, so it's a change for your frontend, not your backend.

ActiveDirectoryLdapAuthenticationProvider and authentication using userDetailsService

I have two different users in my application. Ldap users and api users. Ldap users have privilege to access an endpoint and api users a different endpoint. I have implemented the api user authentication using UserDetailsService and having the details in my application.yaml file.
The issue I am facing now is, The endpoint that only Ldap users should access is now being accessed my api users as well. How can I prevent this. Please find my code snippet below
public class ServiceSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("ldapProvider")
private AuthenticationProvider authenticationProvider;
#Override
protected void configure(HttpSecurity http) throws Exception {
// security for apiuser
http
.authorizeRequests()
.antMatchers(“/abcd/**).hasRole(“admin”)
.and()
.httpBasic().and().userDetailsService(userDetailsService());
// security for ldap users
http
.csrf().disable()
.authorizeRequests()
.antMatchers(“/ghhgh” + "/**").fullyAuthenticated()
.antMatchers("/login*").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().and()
.authenticationProvider(authenticationProvider)
.exceptionHandling();
}
public UserDetailsService userDetailsService() {
UserDetails user = User.withUsername(“api”)
.password(passwordEncoder().encode(“test”))
.roles(“admin”)
return new InMemoryUserDetailsManager(user);
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
In spring security it is indeed possible to register multiple authentication mechanisms.
BUT you cannot register a specific authentication provider to a specific route.
The spring securty docs say:
ProviderManager is the most commonly used implementation of AuthenticationManager. ProviderManager delegates to a List of AuthenticationProviders. Each AuthenticationProvider has an opportunity to indicate that authentication should be successful, fail, or indicate it cannot make a decision and allow a downstream AuthenticationProvider to decide.
So in every request, the registered AuthenticationProviders are checked one after the other until one is successful, or all fail.
To solve your problem, you need to define multiple custom authorities, that you assign your users.
Then you secure your endpoints using these authorities.
E.g. you give every ldap user the authority LDAP_USER and every api user the authority API_USER. Then you configure your security accordingly:
Register all AuthenticationProviders:
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(ldapProvider);
auth.userDetailsService(userDetailsService());
}
And configure the endpoints:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
(...)
.authorizeRequests()
// security for apiuser
.antMatchers(“/abcd/**).hasRole(“API_USER”)
// security for ldap users
.antMatchers(“/ghhgh” + "/**").hasRole("LDAP_USER")
(...)
}

Spring boot security - inMemoryAuthentication not working

Here is my security configuration code:
#EnableWebSecurity
#EnableGlobalMethodSecurity (
prePostEnabled=true
)
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
auth.inMemoryAuthentication()
.withUser("chandra").password("{noop}1234").roles("USER").and()
.withUser("admin").password("{noop}admin123").roles("ADMIN");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/addItem","/delete").hasRole("ADMIN")
.antMatchers("/getAllItems").hasRole("USER")
.and().csrf().disable().headers().frameOptions().disable()
.and()
.formLogin();
}
}
But while compiling spring is still generating the password for me.
Using generated security password: 49f04bde-ac1f-4e30-870b-ba0dd93d50f3
I checked whether the configuration is being loaded or not by print statements and found the security config is loading. Is there any change I should make to make it work with the given user ids and passwords.
Thanks in advance.
Just a default hint about plain text passwords, you can imagine, what comes next. :-)
Anyway for testing purposes you could define a no operation password encoder like this in your #Configuration class:
To make your prepended noopworking, be sure to expose the following bean:
#Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
For further information, please consult the respective chapter in the reference manual.
As an alternative you could provide the good old password manager itself:
import org.springframework.security.crypto.password.PasswordEncoder;
[…]
#Bean
public NoOpPasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}

Spring Security with OAuth2 and anonymous access

I have my Spring REST API secured with Spring Security and OAuth2, I can successfully retrieve a token and access my APIs. My App defines the OAuth2 client itsself.
Now I want users to have anonymous access on some resources. The use case is really simple: I want my app to be usable without login - but if they are logged in, I want to have access to that principal.
Here is my WebSecurityConfigurerAdapter so far:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/api1").anonymous().and()
.authorizeRequests().antMatchers("/ap2**").permitAll();
}
As soon as I add a second antMatcher/anonymous, it fails to work though, and it doesn't really express my intent either - e.g. I wan't to have anonymous access on api1 GETs, but authenticated on POSTs (easy to do with #PreAuthorize).
How can I make the OAuth2 authentication optional?
I dropped my #EnableWebSecurity and used a ResourceServerConfigurerAdapter like so:
#Configuration
#EnableResourceServer
protected static class ResourceServer extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/api/api1", "/api/api2").permitAll()
.and().authorizeRequests()
.anyRequest().authenticated();
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("my-resource-id");
}
}
/api/api1 may now be called with or without authentication.

Resources