how do I enable the /refresh Actuator endpoint and get around CSRF restrictions? - spring-boot

In the WebSecurityConfig.java in our project
public class WebSecurityConfig extends WebSecurityConfigurererAdapter {
#Override
p void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/**").hasAnyAuthority("USER", "ADMIN")
.and.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
}
I have installed the #RefreshScope on my pojo:
#Configuration
#Data
#RefreshScope
public class MyProperties {
private String myName;
private String myAddress;
private String myCity;
.....
}
Spring Boot Actuator is installed. Spring Security is providing https and SSL. I can get to all of the GET actuator endpoints, but when I try to do a call to http://localhost:8080/actuator/refresh (a POST), I get a 403 Refused.
In looking around, I saw that the default stuff doesn't allow POSTs. This works if I disable csrf(), but not if it is enabled (which is a requirement for the product).
Could someone help me understand what is going on? Has anyone else done this? Can this be done without disabling CSRF entirely?
Thanks,
Winona

Just specify what endpoints on which you want to disable CSRF. For example: csrf().ignoringAntMatchers("/actuator/**"). Doing so disables CSRF prorection for any request that starts with /actuator/.

Related

Spring security login except whitelisted IP

I have a Spring boot application and added Spring security. I would like that some ip ranges by pass the security while all other requests need to log in before. I have extended the WebSecurityConfigurerAdapteras follows however it does not work as I would expect, The whitelisted IP can use the application without login while other requests get an HTTP 403 instead of getting the log in form. What I am doing wrong here?
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure (HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().access("hasIpAddress('192.168.1.0/24') or isAuthenticated()");
}
}

How to access endpoint without token in spring boot?

I am using an actuator for getting server health, also I am doing validation on the token on each request, now I need to put a token to access actuator health but I want to access actuator health without using the token and without affecting API endpoint with token!
Note: My Actuator working fine with the token.
also, I implement the OncePerRequestFilter class for validating the firebase token for each request.
You can create a custom SecurityConfiguration where you permit access to actuator requests:
#Configuration
#EnableWebSecurity
class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.requestMatchers("/actuator/**").permitAll()
.anyRequest().authenticated();
}
}
You may want to read the spring-boot-docs for more information.
EDIT:
When using OncePerRequestFilter you could implement the shouldNotFilter method and check for actuator paths there:
#Override
protected boolean shouldNotFilter(HttpServletRequest request)
throws ServletException {
String path = request.getRequestURI();
return path.startsWith("/actuator");
}

Run a Spring Boot oAuth2 application as resource server AND serving web content

I'm using Spring Boot 1.5.13 and with that Spring Security 4.2.6 and Spring Security oAuth2 2.0.15.
I want to find a best practice setup for our Spring Boot applications that serve a mixed set of content: A REST API, and some web pages that provide a convenience "landing page" for developers with some links on it, plus Swagger based API documentation, which is also web content.
I have a configuration that allows me to run the app with proper authorization code flow, hence I can access all web content via Browser and get authenticated by the configured IdP (in my case PingFederate), plus I can make API calls from within the Browser, i.e. directly or with a REST Client, e.g. with RESTClient.
This is my security configuration:
#Slf4j
#Configuration
#EnableWebSecurity
#EnableOAuth2Sso // this annotation must stay here!
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login**", "/webjars/**", "/css/**").permitAll()
.antMatchers("/cfhealth").permitAll()
.antMatchers("/").permitAll()
.antMatchers("/protected", "/api/**").authenticated();
}
#Bean
public RequestContextListener requestContextListener() {
return new RequestContextListener();
}
}
and the oAuth2 configuration:
#Configuration
#Slf4j
public class OAuth2Config extends ResourceServerConfigurerAdapter {
#Value("${pingfederate.pk-uri}")
String pingFederatePublicKeyUri;
#Autowired
PingFederateKeyUtils pingFederateKeyUtils;
#Override
public void configure(ResourceServerSecurityConfigurer config) {
config.tokenServices(tokenServices());
}
#Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
String certificate = pingFederateKeyUtils.getKeyFromServer(pingFederatePublicKeyUri);
String publicKey = pingFederateKeyUtils.extractPublicKey(certificate);
converter.setVerifier(pingFederateKeyUtils.createSignatureVerifier(publicKey));
return converter;
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
return defaultTokenServices;
}
}
But when I want to call a REST API programmatically/outside the Browser with a bearer token in the header, e.g. with curl, the authorization code flow kicks in and redirects to the local login endpoint. What I want is that API calls accept the bearer token for authentication, without creating a session, and that all web content/mvc calls in the Browser establish a session.
curl -i -H "Accept: application/json" -H "Authorization: Bearer $TOKEN" -X GET http://localhost:8080/authdemo/api/hello
Adding the #EnableResourceServer annotation to the above SecurityConfig class (and adding security.oauth2.resource.filter-order=3 in the application properties file, I can make the curl command work, but then the authorization code flow is broken, I get the following output in the Browser for all URLs in my application:
<oauth>
<error_description>
Full authentication is required to access this resource
</error_description>
<error>unauthorized</error>
</oauth>
Now is there a way to get this szenario working nicely? If yes, how would that look like? Or is it only supported in later versions of Spring Boot+Security+oAuth2?
The question at Spring Boot with Security OAuth2 - how to use resource server with web login form? is quite similar
I found the solution: It takes multiple HttpSecurity configurations. I found out by reading the great article written by Matt Raible at https://developer.okta.com/blog/2018/02/13/secure-spring-microservices-with-oauth where he introduced me to the notion of requestMatchers(.). This is how I finally implemented it:
#Configuration
#EnableResourceServer
#EnableWebSecurity(debug = true)
#EnableOAuth2Sso
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Bean
public RequestContextListener requestContextListener() {
return new RequestContextListener();
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.requestMatcher(new RequestHeaderRequestMatcher("Authorization"))
.authorizeRequests().anyRequest().fullyAuthenticated();
}
}
With that I can access the service with a Browser, leading to a authorization code flow. But accessing the API (or actually any part of the service) leads to a validation of the provided Bearer token.
And to illustrate the way how some endpoints can be exluded/made public in such a case, here's how I configure the actuator endpoints and one very simple 'ping' endpoint I've added myself:
#Configuration
#Order(1)
public class ActuatorSecurity extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatcher(new OrRequestMatcher(EndpointRequest.to("health", "info"),
new AntPathRequestMatcher("/cfhealth"))).authorizeRequests().anyRequest().permitAll();
}
}
And my implementation of the /cfhealth endpoint:
#Controller
#Slf4j
public class MainController {
#GetMapping(value = "/cfhealth")
#ResponseBody
public String cfhealth() {
return "ok";
}
}
I'm happy to learn from others if that's the best practice way of Spring Security configuration or if there are better ways to do it. I've spent quite some time on the topic in the last few weeks on it, and it takes quite some effort to grasp the basic Spring Security concepts.

Spring Boot 2.0 disable default security

I want to use Spring Security for JWT authentication. But it comes with default authentication. I am trying to disable it, but the old approach of doing this - disabling it through application.properties - is deprecated in 2.0.
This is what I tried:
#Configuration
public class StackWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().disable();
// http.authorizeRequests().anyRequest().permitAll(); // Also doesn't work.
}
}
How can I simply disable basic security?
UPDATE
It might be nice to know that I am not using web mvc but web flux.
Screenshot:
According to the new updates in Spring 2.0, if Spring Security is on the classpath, Spring Boot will add #EnableWebSecurity.So adding entries to the application.properties ain't gonna work (i.e it is no longer customizable that way). For more information visit the official website Security changes in Spring Boot 2.0
Albeit not sure about your requirement exactly, I could think of one workaround like the following:-
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception{
http.authorizeRequests().antMatchers("/").permitAll();
}
}
Hope this helps.
From Spring Boot 2.1 on, if you include spring-boot-actuator, it does not suffice anymore to only exclude SecurityAutoconfiguration, you also need to exclude ManagementWebSecurityAutoConfiguration, like so:
#SpringBootApplication(exclude = { SecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class })
According to the reference documentation, the Security configuration for allowing all requests with WebFlux should look like this:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
#Configuration
public class SecurityConfig {
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http.authorizeExchange().anyExchange().permitAll();
return http.build();
}
}
This worked for me:
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().anyRequest().permitAll();
}
}
You can add/modify the following to your Application class:
#SpringBootApplication(exclude = { SecurityAutoConfiguration.class })
public class MyApplication {
}
Adding some fresh answer, I assume all use actuator, if not I'd bet one class exclusion should be sufficient, I managed to disable through properties:
spring:
autoconfigure:
exclude: ${spring.autoconfigure.sac}, ${spring.autoconfigure.mwsas}
sac: org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
mwsas: org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration
I've referenced two auto-config classes through property to keep the length intact (note that IntelliJ Ultimate will cry if you reference it like that as it has no clue what are these placeholder values and if they are actually legit classes, so inline if that annoys you).
Application however does not fail to start as claimed by:
https://www.baeldung.com/spring-boot-security-autoconfiguration
if you just disable SecurityAutoConfiguration
If it did work, you will stop seeing auto generated password and it is a little bit less confusing than the accepted answer, as dev reading the log won't get confused by generated password for basic auth while security allows all.
Why just disabling main auto config class isn't enough is because of this fella:
#Configuration
class ManagementWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.requestMatchers(
EndpointRequest.to(HealthEndpoint.class, InfoEndpoint.class))
.permitAll().anyRequest().authenticated().and().formLogin().and()
.httpBasic();
}
}
There was tons of work made to split actuator and security config which confused us all, now its more straightforward but artifacts like these still exist. Spring devs will correct me if I am wrong :-).
I have leveraged #ConditionalOnProperty to load the following SecurityConfig.java class if I set spring.security.enabled property to false in my application.yml to disable spring security and it works like a charm.
#ConditionalOnProperty(name = "spring.security.enabled", havingValue = "false")
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests().antMatchers("/").permitAll();
}
}
If anyone is struggling with this in a WebFlux based application, or a Spring Cloud Gateway application, the below worked for me:
#EnableWebFluxSecurity
public class InsecurityConfiguration {
// #formatter:off
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
.authorizeExchange()
.anyExchange().permitAll();
return http.build();
}
}
To disable default security for Spring Boot Reactive Web applications, use the following excludes when you have actuator also in the classpath.
#SpringBootApplication(exclude = {ReactiveSecurityAutoConfiguration.class, ReactiveManagementWebSecurityAutoConfiguration.class })
I think what you are looking for is to override the default authentication entry point which is set to BasicAuthenticationEntryPoint.
This entrypoint adds the
"WWW-Authenticate": "Basic realm=..."
header that tells your browser to use Basic Auth.
If you're extending WebSecurityConfigurerAdapter, you can pass in true to the super constructor to disable the defaults.
You may need to provide other beans if you do this.
/**
* Creates an instance which allows specifying if the default configuration should be
* enabled. Disabling the default configuration should be considered more advanced
* usage as it requires more understanding of how the framework is implemented.
*
* #param disableDefaults true if the default configuration should be disabled, else
* false
*/
protected WebSecurityConfigurerAdapter(boolean disableDefaults) {
this.disableDefaults = disableDefaults;
}
If you want to disable it just for testing purposes -
Rather than completely disabling the auto-configuration, I create an "InsecurityConfiguration" in addition to "SecurityConfiguration", and activate it with either a Spring Profile or Property value.
Technically security is still configured, but wide open.
#Configuration
#ConditionalOnProperty(prefix = "security", value = "disabled", havingValue = "true")
public class InsecurityConfiguration extends WebSecurityConfigurerAdapter {
private final static Logger log = LoggerFactory.getLogger(InsecurityConfiguration.class);
#Override
protected void configure(HttpSecurity http) throws Exception {
log.warn("configuring insecure HttpSecurity");
http.authorizeRequests().anyRequest().permitAll();
}
#Override
public void configure(WebSecurity web) throws Exception {
log.warn("configuring insecure WebSecurity");
web.ignoring().antMatchers("/**");
}
}
Note This is for mvc, not webflux. For Webflux you should create a SecurityWebFilterChain like Bryan mentioned.
This is how I generally disable basic auth in webflux, when using JWT -
#Bean
public SecurityWebFilterChain configure(ServerHttpSecurity http) {
http
.authorizeExchange().anyExchange().authenticated().and()
.httpBasic().disable()
.formLogin().disable()
.logout().disable()
.oauth2ResourceServer()
.jwt()
.and()
.and().exceptionHandling().accessDeniedHandler(problemSupport);
return http.build();
}
Only properties - works for me (sb2 - 2022):
spring:
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
- org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration
Simple solution for Spring Boot 2.6
#SpringBootApplication(exclude = {SecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class, UserDetailsServiceAutoConfiguration.class})
In Spring boot 2, there is no way to disable basic authentication by application.properties file. But the only thing is use annotation
#EnableAutoConfiguration(exclude = {SecurityAutoConfiguration.class})
in the main class.
It works
The problem is with org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter
it has private ServerAuthenticationEntryPoint authenticationEntryPoint = new HttpBasicServerAuthenticationEntryPoint();
so to fix it during ServerHttpSecurity initialization add:
http.exceptionHandling().authenticationEntryPoint(HttpStatusServerEntryPoint(HttpStatus.FORBIDDEN))
Looks like vanilla (servlet) spring uses org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer#createDefaultEntryPoint
private AuthenticationEntryPoint createDefaultEntryPoint(H http) {
if (this.defaultEntryPointMappings.isEmpty()) {
return new Http403ForbiddenEntryPoint();
}
if (this.defaultEntryPointMappings.size() == 1) {
return this.defaultEntryPointMappings.values().iterator().next();
}
DelegatingAuthenticationEntryPoint entryPoint = new DelegatingAuthenticationEntryPoint(
this.defaultEntryPointMappings);
entryPoint.setDefaultEntryPoint(this.defaultEntryPointMappings.values().iterator()
.next());
return entryPoint;
}
Side note: mutable fields in builder style beans (like ExceptionTranslationWebFilter) make spring code hard to debug (too magic configuration as well)
You should add #EnableWebSecurity to enable a custom security configuration.
After that simply disable the form login
#Configuration
#EnableWebSecurity
public class StackWebSecurityConfigurerAdapter extends
WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin().disable();
}
}
This worked for me
#SpringBootApplication(exclude = {UserDetailsServiceAutoConfiguration.class})
class SpringApplication{
...
}

Spring Security OAuth2 check_token endpoint

I'm trying to setup a resource server to work with separate authorization server using spring security oauth. I'm using RemoteTokenServices which requires /check_token endpoint.
I could see that /oauth/check_token endpoint is enabled by default when #EnableAuthorizationServer is used. However the endpoint is not accessible by default.
Should the following entry be added manually to whitelist this endpoint?
http.authorizeRequests().antMatchers("/oauth/check_token").permitAll();
This will make this endpoint accessible to all, is this the desired behavior? Or am I missing something.
Thanks in advance,
You have to
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception
{
oauthServer.checkTokenAccess("permitAll()");
}
For more information on this ::
How to use RemoteTokenService?
Just to clarify a couple of points, and to add some more information to the answer provided by Pratik Shah (and by Alex in the related thread):
1- The configure method mentioned is overridden by creating a class that extends AuthorizationServerConfigurerAdapter:
#EnableAuthorizationServer
#Configuration
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("ger-client-id")
.secret("ger-secret")
.authorizedGrantTypes("password")
.scopes("read", "write");
}
}
2- I suggest reading this Spring guide explaining the automatic configuration carried out by Spring Boot when we include the #EnableAuthorizationServer annotation, including an AuthorizationServerConfigurer bean. If you create a configuration bean extending the AuthorizationServerConfigurerAdapter as I did above, then that whole automatic configuration is disabled.
3- If the automatic configuration suits you just well, and you JUST want to manipulate the access to the /oauth/check_token endpoint, you can still do so without creating an AuthorizationServerConfigurer bean (and therefore without having to configure everything programmatically).
You'll have to add the security.oauth2.authorization.check-token-access property to the application.properties file, for example:
security.oauth2.client.client-id=ger-client-id
security.oauth2.client.client-secret=ger-secret
security.oauth2.client.scope=read,write
security.oauth2.authorization.check-token-access=permitAll()
Of course, you can give it an isAuthenticated() value if you prefer.
You can set the log level to DEBUG to check that everything is being configured as expected:
16:16:42.763 [main] DEBUG o.s.s.w.a.e.ExpressionBasedFilterInvocationSecurityMetadataSource - Adding web access control expression 'permitAll()', for Ant [pattern='/oauth/check_token']
There is no much documentation about these properties, but you can figure them out from this autoconfiguration class.
One last thing worth mentioning, even though it seems to be fixed in latest Spring versions, I just submitted an issue in the spring-security-oauth project; it seems that the token_check functionality is enabled by default if you add a trailing slash to the request:
$ curl localhost:8080/oauth/check_token/?token=fc9e4ad4-d6e8-4f57-b67e-c0285dcdeb58
{"scope":["read","write"],"active":true,"exp":1544940147,"authorities":["ROLE_USER"],"client_id":"ger-client-id"}
There are three POST parameters, namely client_id (user name), client_secret (password corresponding to the user name), token (token applied for), client_id, client_secret are different from the parameters in the /oauth/token interface
enter image description here
First, config token access expression:
#Override
public void configure(AuthorizationServerSecurityConfigurer securityConfigurer) throws Exception {
securityConfigurer
.allowFormAuthenticationForClients()
.checkTokenAccess("isAuthenticated()")
.addTokenEndpointAuthenticationFilter(checkTokenEndpointFilter());
}
Then, we need define a filter to process client authentication:
#Bean
public ClientCredentialsTokenEndpointFilter checkTokenEndpointFilter() {
ClientCredentialsTokenEndpointFilter filter = new ClientCredentialsTokenEndpointFilter("/oauth/check_token");
filter.setAuthenticationManager(authenticationManager);
filter.setAllowOnlyPost(true);
return filter;
}

Resources