Spring Security : request mapped to /error - spring

I am using Spring Boot with Spring Security. Request TRACE http://localhost:8080/invalid/path gets mapped /error in my spring boot application. Can I know where exactly this request gets mapped to /error. I want to handle the request to send custom response. I enabled the debugging for web security by the following line.
public void configure(WebSecurity web) throws Exception {
web.debug(true);
}
Output of curl -D - -X TRACE http://localhost:8081/invalid/path
HTTP/1.1 405
Allow: HEAD, DELETE, POST, GET, OPTIONS, PUT
Content-Type: message/http
Content-Length: 83
Date: Thu, 14 May 2020 06:24:25 GMT
TRACE /error HTTP/1.1
host: localhost:8104
user-agent: curl/7.64.1
accept: */*
The application log shows the following
************************************************************
Request received for TRACE '/error':
org.apache.catalina.core.ApplicationHttpRequest#7dba82cf
servletPath:/error
pathInfo:null
headers:
host: localhost:8104
user-agent: curl/7.64.1
accept: */*
Security filter chain: [] empty (bypassed by security='none')
************************************************************
My Custom Firewall ignores the RequestRejectedException
#Override
public FirewalledRequest getFirewalledRequest(final HttpServletRequest request) {
try {
return super.getFirewalledRequest(request);
} catch (RequestRejectedException ex) {
return new FirewalledRequest(request) {
#Override
public void reset() {}
};
}
}
Any help on this ?
Using Spring Boot 2.2.6

I faced the same issue, here is the solution:
Adding this config into the WebSecurity configuration.
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.authorizeRequests().antMatchers("/error/**").permitAll();
}

Related

SecurityFilterChain .anyRequest().permitAll() doesn't allow POST requests

Using Spring Boot 2.7 (Spring Security 5.7.1) and trying to configure an API as a resource server and OAuth2 client I find a behavior I don't get to understand:
#EnableWebSecurity
public class SecurityConfig {
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.mvcMatchers("/swagger-ui/**", "/api-docs/**").permitAll()
.anyRequest().permitAll())
// register OAuth2 resource server
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
// register OAuth2 client
.oauth2Client(withDefaults());
return http.build();
}
}
Checking the logs, all this filters apply
o.s.s.web.DefaultSecurityFilterChain : Will secure any request with
org.springframework.security.web.session.DisableEncodeUrlFilter#320ca97c,
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter#3c592c0c,
org.springframework.security.web.context.SecurityContextPersistenceFilter#2b33e616,
org.springframework.security.web.header.HeaderWriterFilter#2e9bff08,
org.springframework.security.web.csrf.CsrfFilter#7926d092,
org.springframework.security.web.authentication.logout.LogoutFilter#37227aa7,
org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter#6f18445b,
org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter#42af2977,
org.springframework.security.web.savedrequest.RequestCacheAwareFilter#79e3f444, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter#1252d480,
org.springframework.security.web.authentication.AnonymousAuthenticationFilter#3979c6e8,
org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter#19faa9dc,
org.springframework.security.web.session.SessionManagementFilter#7d3b4646,
org.springframework.security.web.access.ExceptionTranslationFilter#6cb2d5ea,
So far, this configuration works as expected in the other APIs I'm protecting. However, in this particular one, AND not having protected any endpoint I see that:
I can access any GET endpoint but any POST endpoint returns a 403 FORBIDDEN. However, I can access them if I add a token to the request EVEN if its an expired token
This alone I can't understand as .anyRequest().permitAll() should unprotect anything, if I'm not wrong.
If I comment out the line configuring the oauth2 ResourceServer
`// .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)`
this filter dissapears
org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter
And can't use POST endpoints anymore, even with the expired token
Logically, I want the API to be oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt) so
How it is I can't access POST endpoints when using .anyRequest().permitAll())?
DISCLAIMER: I know it makes no sense to declare the API as resource server if all endpoints must be public. Endpoints will be accessed by Discord callbacks and I have to figure out if I can protect them with OAuth
EDIT:
server.servlet.context-path = /api
Controller
#RestController
#RequestMapping("/slack")
public class SlackBotController {
#PostMapping("/test")
public String test(#RequestBody String a) {
return a;
}
#GetMapping("/test")
public String testGet() {
return "OK";
}
}
Request
GET/POST http://localhost:8081/api/slack/test
Request headers
User-Agent: PostmanRuntime/7.29.0
Accept: */*
Postman-Token: f20ba7a6-26e5-47c4-a827-0596afec27b8
Host: localhost:8081
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 3
Cookie: JSESSIONID=D1C2B2668DC130C63DDE03F30574ED5F; JSESSIONID=823D79956CFBF14F3C77C96E29F4131C
Response headers
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: application/json
Transfer-Encoding: chunked
Date: Fri, 03 Jun 2022 12:03:00 GMT
Keep-Alive: timeout=60
Connection: keep-alive
The reason why the POST endpoint or the POST returns a 403 FORBIDDEN because the CSRF protection is enabled by default in spring.
That means that every modifying request (POST, PUT, DELETE, PATCH) requires a CSRF token. Otherwise the request gets denied to prevent CSRF attacks.
To disable the CSRF protection:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable();
}
The problem here is the CSRF protection. When you use http.oauth2ResourceServer() Spring Security configures CSRF to ignore requests that contains the header Authorization: Bearer whatever, note that it has to contain the Bearer prefix.
In the request sample that you shared, you are not including the Authorization header.
Take a look at the code in Spring Security.

Secure specific endpoint by role

I'm trying to build REST API with Spring Boot, secured by Spring Security. Here I need to provide /users endpoint which will be available only to users with ADMIN role.
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
SecurityContext context = SecurityContextHolder.createEmptyContext();
Authentication authentication =
new TestingAuthenticationToken("username", "password", "ROLE_ADMIN");
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context);
http.authorizeRequests()
.antMatchers("/users").hasRole("ADMIN")
.antMatchers("/products").permitAll()
;
}
}
I'm using TestingAuthenticationToken with ROLE_ADMIN, so I expect that /users endpoint will be available in this configuration.
Request:
GET /users HTTP/1.1
Host: localhost:5000
Accept: application/json
Content-Type: application/json
Cache-Control: no-cache
Response:
"timestamp": "2020-09-01T17:28:27.628+00:00",
"status": 403,
"error": "Forbidden",
"message": "Access Denied",
"path": "/users"
}
The SecurityContext with its Authentication is retrieved during each request in the SecurityContextPersistenceFilter. Your SecurityContext in the SecurityConfig is hence simply overwritten (with auth == null). No authentication means no role hence 403 - forbidden.
For more see here.

Spring WS not accepting basic auth credentials

I've been struggling with this one for a while and can't figure out what I'm doing wrong.
I'm supposed to add basic authentication to my SOAP web service in Spring. I made the security config pretty simple (maybe too simple) so it concentrates on basic auth only (see below).
When I'm accessing the base URL from the browser, the authentication seems to be working, it asks for the credentials and if I provide them correctly, it accepts them.
However, when I want to send the SOAP request that contains the basic auth header to my web service endpoint, Spring Security sends back 401 to me. I tried sending the request with SOAPUI, Postman and from Windows Powershell via Invoke-WebRequest, and the result is the same whereas if I catch the request with Wireshark, the right header is there.
I'm using Spring Boot 2.1.8 for this project (same version with Spring Web Services and Security).
The security config class:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("foo")
.password(passwordEncoder().encode("bar"))
.roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and().httpBasic();
}
#Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
To my understanding, there is nothing specific I have to add to the web service config itself so all related basic auth settings can be done in the security config class. Or am I wrong?
Appreciate your help.
Update
Here is the request/response pair:
REQUEST:
POST /foo/endpoint/ HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: text/xml;charset=UTF-8
SOAPAction: "http://foo.bar"
Authorization: Basic Zm9vOmJhcg==
Content-Length: 9688
Host: localhost:1502
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:foo="http://foobar.com/">
<soapenv:Header/>
<soapenv:Body>
// body omitted
</soapenv:Body>
</soapenv:Envelope>
RESPONSE
HTTP/1.1 401
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
WWW-Authenticate: Basic realm="Realm"
Content-Length: 0
Date: Thu, 13 Aug 2020 14:22:29 GMT
configure(HttpSecurity http) needs some modifications to enable the http basic authentication in your code.
authorizeRequests() is used for authorization purposes. Once after a user successfully logged in, what all the resources(endpoints) should be accessible to logged-in user, is defined by authorizeRequests(). I also recommend, while authorizing the endpoints, you better use the antMatchers. For Example: I have modified the above code as
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("foo")
.password(passwordEncoder().encode("bar"))
.roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and()
.authorizeRequests()
.antMatchers("/simple/**").hasRole("USER");
}
#Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
I hope this would work for you. Try & let me know.
NOTE: In Spring boot, just by adding the spring-boot-starter-security dependency, security gets enabled, without any configurations. So now, you trying to re-configure the basic auto-configuration. By extending, WebSecurityConfigurerAdapter class. That's correct.
POSTMAN: use authorization header

How to remove authentication from WebSecurityConfigurerAdapter

I have the class below which uses antMatchers to remove authentication from a public endpoint.
However the public endpoint is also being blocked and I keep getting a HTTP/1.1 401.
Can anyone please help me to spot what's wrong below?
#Configuration
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
#Autowired
private UsersService usersService;
#Autowired
private UsersRepository usersRepo;
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder()
{
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable();
http.authorizeRequests().antMatchers(HttpMethod.POST, "/public").permitAll()
.anyRequest().authenticated()
.and().addFilter(getAuthenticationFilter());
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(usersService).passwordEncoder(bCryptPasswordEncoder());
}
private AuthenticationFilter getAuthenticationFilter() throws Exception {
final AuthenticationFilter filter = new AuthenticationFilter(authenticationManager(),
usersService);
return filter;
}
}
---------------update 1-------------------
I tried with http POST, using curl and I get back the below.
It seems like the request is caught somewhere but not in the controller I am trying to hit:
$ curl -X POST http://localhost:8083/public -H 'Content-Type:
application/json' -H 'cache-control: no-cache' -d '{
"email":"test2#test.com", "password":"12345678" }' -v
* Trying ::1:8083...
* Connected to localhost (::1) port 8083 (#0)
> POST /user HTTP/1.1
> Host: localhost:8083
> User-Agent: curl/7.69.1
> Accept: */*
> Content-Type: application/json
> cache-control: no-cache
> Content-Length: 51
>
* upload completely sent off: 51 out of 51 bytes
* Mark bundle as not supporting multiuse
< HTTP/1.1 401
< Set-Cookie: JSESSIONID=72AB25425322A17AE7014832D25284FD; Path=/;
HttpOnly
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< WWW-Authenticate: Basic realm="Realm"
< Content-Length: 0
< Date: Tue, 31 Mar 2020 11:36:10 GMT
<
* Connection #0 to host localhost left intact
You might want to override the WebSecurity method to completely ignore your /public path from Spring Security processing.
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/public/**");
}
It is difficult to know without seeing all of your code, but I suspect that this has nothing to do with the authorizeRequests() portion of your configuration. Instead, I suspect it is the AuthenticationFilter which is attempting to authenticate the request because you have included credentials in the request. The default is to try to authenticate anytime the AuthenticationConverter returns credentials. Then AuthenticationFailureHandler will respond with HTTP 401 if invalid credentials are provided.
To resolve this, you can remove the credentials from your request. Alternatively, you can limit which requests AuthenticationFilter are invoked on by setting the requestMatcher. Something like this would limit to processing POST to /authenticate:
private AuthenticationFilter getAuthenticationFilter() throws Exception {
final AuthenticationFilter filter = new AuthenticationFilter(authenticationManager(),
usersService);
filter.setRequestMatcher(new AntPathRequestMatcher("/authenticate", "POST"));
return filter;
}

Spring MVC Trace Http Request

I want to disable the Trace verb on the serve so that endpoints are untraceable.
My endpoints are created using Spring MVC and there is a option endpoints.trace.enabled=false.
Pain is how to test this, if trace is disabled or not. I tried chrome plug-ins live HTTP Headers but it for generic site. I want to check my endpoint.
For example:
curl --insecure -v -X TRACE -H http://localhost:8080/toy/49f6a7d3-eb20-3ab2-be3b-8399e7f28abf
HTTP/1.1 200 OK
< Date: Sat, 21 Mar 2015 04:34:03 GMT
< Content-Type: message/http; charset=UTF-8
< Content-Length: 270
<
TRACE /toy/49f6a7d3-eb20-3ab2-be3b-8399e7f28abf HTTP/1.1
User-Agent: curl/7.37.1
Host: localhost:8080
Accept: */*
* Connection #0 to host localhost left intact
I tried below but now it denies all the request.
#Configuration
#EnableWebMvcSecurity
#EnableGlobalMethodSecurity(securedEnabled=true, prePostEnabled = true, jsr250Enabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.and()
.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.TRACE, "/**").denyAll();
}
}
AFAIK TRACE is disabled by default on Tomcat, as well as Jetty 6,
You can use curl to make sure:
curl -v -X TRACE http://www.example.com
You want to see 405 method not allowed in the response.

Resources