Small question regarding a SonarQube scan on a SpringBoot project please.
I have a very simple handler, super simple, as follow:
#ResponseBody
#RequestMapping(method = { RequestMethod.GET, RequestMethod.POST}, path = "/my/api")
public Mono<String> question() {
return myService.dosomething();
}
I have Spring Security in my class path, this project is a Spring WebFlux Application using 2.6.7 and Java 11.
Upon static analysis scan, I am being flagged with:
Spring CSRF unrestricted RequestMapping
Description
<p>Methods annotated with <code>RequestMapping</code> are by default mapped to all the HTTP request methods.
However, Spring Security's CSRF protection is not enabled by default
for the HTTP request methods <code>GET</code>, <code>HEAD</code>, <code>TRACE</code>, and <code>OPTIONS</code>(as this could cause the tokens to be leaked).
Therefore, state-changing methods annotated with <code>RequestMapping</code> and not narrowing the mapping
to the HTTP request methods <code>POST</code>, <code>PUT</code>, <code>DELETE</code>, or <code>PATCH</code>are vulnerable to CSRF attacks.</p><p> <b>Vulnerable Code:</b><br/><pre><code>#Controller
public class UnsafeController {
...
<b>References</b><br/>Spring Security Official Documentation: Use proper HTTP verbs (CSRF protection)<br/>OWASP: Cross-Site Request Forgery<br/>OWASP: CSRF Prevention Cheat Sheet<br/>CWE-352: Cross-Site Request Forgery (CSRF)</p>
I do not understand this issue.
What I tried:
I tried splitting the Controller onto two different controllers, one for each verb, and for some reason, this fix the issue:
#ResponseBody
#GetMapping(path = "/my/api")
public Mono<String> questionGet() {
return myService.dosomething();
}
#ResponseBody
#PostMapping(path = "/my/api")
public Mono<String> questionPost() {
return myService.dosomething();
}
But I am now carrying a duplicate, therefore, I would like to stay with my:
#ResponseBody
#RequestMapping(method = { RequestMethod.GET, RequestMethod.POST}
I also added Spring Security to protect myself against CSRF, but no luck, the issue still persist.
May I ask what is the proper way to fix this please?
Thank you
Related
In Spring Boot Controller implementation we can get the JwtAuthenticationToken as a parameter in our method. Same token can be read, manipulated and validated for authorization like below
#PostMapping("/hello")
#PreAuthorize("hasAuthority('SCOPE_Internal') or hasAuthority('ROLE_User')")
public Mono<String> testHello(JwtAuthenticationToken token) {
log.info("token is " + token.getTokenAttributes().toString());
return Mono.just("OK");
}
We are using reactive Spring Boot and we have replaced our controllers with RouterFunction. We are wondering how above feature - Authorization and get the token in our router method calls.
public RouterFunction<ServerResponse> route() {
return RouterFunctions.route(GET("/hello"), helloHandler::testHello);
}
When we tried passing the JwtAuthenticationToken in the router method call, it threw
Could not autowire. No beans of 'JwtAuthenticationToken' type found.
public RouterFunction<ServerResponse> route(JwtAuthenticationToken jwtAuthenticationToken) {
return RouterFunctions.route(GET("/hello"), helloHandler::testHello);
}
We came up this solution if it makes any sense, or valid. We ran into same issue lately as we began a journey of converting our legacy and synchronous spring boot server app to an asynchronous one. The JwtAuthenticationToken which we use to get some added attribute to the token used by the system works out of the box when we used the RestController and pass it as an argument in the protected endpoint method. But with Router Function we could not get it to work. After 1 day of painstaking research on google, stackoverflow, spring's reactive application resource server docs, we could not get any head way. However, this post got us thinking even more. so we came up with this solution:
#Slf4j
#Component
#RequiredArgsConstructor
public class FitnessAccountWebHandler {
private final FitnessAccountService accountService;
public Mono<ServerResponse> getAccountByUserId(ServerRequest request) {
String userId = request.pathVariable(AccountResourceConfig.USER_ID);
// This will give you the JwtAuthenticationToken if a Principal is
// returned or empty if no authentication is found
Mono<JwtAuthenticationToken> authentication = request
.principal()
.cast(JwtAuthenticationToken.class);
return authentication.flatMap(auth -> {
log.info("Subject: {}", auth.getName());
return accountService.getSomething(userId)
.flatMap(ServerResponse.ok()::bodyValue)
.switchIfEmpty(ServerResponse.notFound().build());
});
}
}
Hope this helps someone and save some time.
I am currently trying to shift my application from spring boot 1.5.x to 2.x.x on reactive stack. I am facing a kinda weird problem that I can't figure out. Hope someone knows the solution to this.
I implemented an api to receive a user jwt token as "Authorization" field on the header. The api is a POST method that receives a certain json data from the user in the body, goes to the backend and processes it.
The unfortunate thing is i keep getting a http 404 error when i add in the header, a normal 200 when i remove it in postman.
Here is my controller.
#RestController
#RequestMapping("/user")
#Slf4j
public class UserHandler {
#Autowired
private UserService service;
#Autowired
private Utility utility;
#PostMapping("/updateLink")
public Mono<ServerResponse> addNewAccountLinkAPI(#RequestHeader(name="Authorization") String id, #RequestBody UpdateAccountLink request){
return Mono.just(request)
.flatMap(s -> service.addNewAccountLink(s))
.flatMap(s -> ok().body(BodyInserters.fromObject(new RespWrap("Success", new Date(), null, s))))
.switchIfEmpty(badRequest().body(BodyInserters.fromObject(new RespWrap("Failed", new Date(), "Failed to create new link", null))));
}
}
Here is my simple security config
#Configuration
#EnableWebFluxSecurity
#EnableWebFlux
public class ResourceServerConfig implements WebFluxConfigurer {
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http, FirebaseAuthenticationManager manager) {
http
.authorizeExchange().anyExchange().permitAll()
.and().csrf().disable();
return http.build();
}
}
Can anyone please point me out on the problem. This sure seems like a lack of config problem.
I can see two issues with your code snippets.
First, you shouldn't add #EnableWebFlux as it completely disables the auto-configuration done by Spring Boot. Same goes for #EnableWebMvc in a Spring MVC application.
Second, you're mixing WebFlux annotations and WebFlux functional. The annotations you're using are fine, but the ServerResponse type should only be used when writing functional handlers. You should try instead here to use ResponseEntity.
I am using:
Spring 4.1.1.RELEASE
Spring Security 3.2.5.RELEASE
spring-security-oauth2 1.0.0.RELEASE
I have created a two multipart request:
One is at Non-secure controller Second at a Secure controller.
Both are same, there is no change.
Non secured request works very fine but secured Multipart request not working
#RequestMapping(value="/profileimage", method=RequestMethod.POST)
public #ResponseBody String createProfilePicture(#RequestParam MultipartFile
file, #RequestParam String profileId){
}
Please reply if you require more information.
Please specify what exactly you are trying to achieve by implementing an oauth2 authorization server.
Do you plan to support various authentication providers e.g Facebook, Linkedin, Google?
Please specify the error message you get when trying to consume the API.
Please share your Security configuration classes i.e the class that extends WebSecurityConfigurerAdapter and any other configuration that you have.
You can review this article which details how to implement an oauth2 authorization server to make sure you haven't missed any part.
In regards to the code above, it seems corrent but can be simplified:
#PostMapping("/profileimage")
public #ResponseBody String createProfilePicture(#RequestParam MultipartFile
file, #RequestParam String profileId){
}
You can even drop the #ResponseBody annotation in case a #RestController
annotation is specified on the class.
Good luck!
I'm reading through this Spring security tutorial and it mentions that we might see 2 requests for the dynamic resource because there is a CORS negotiation for this controller:
#SpringBootApplication
#RestController
public class UiApplication {
#RequestMapping("/resource")
public Map<String,Object> home() {
Map<String,Object> model = new HashMap<String,Object>();
model.put("id", UUID.randomUUID().toString());
model.put("content", "Hello World");
return model;
}
public static void main(String[] args) {
SpringApplication.run(UiApplication.class, args);
}
}
Just curious as to whether the #RestController annotation enables CORS by default?
No.
You need to add #CrossOrigin annotation by yourself to get CORS Support in Spring.
Why:
Enabling CORS (Cross-origin resource sharing) by default will be a serious security issue.
Consider this example from official docs.
For security reasons, browsers prohibit AJAX calls to resources
residing outside the current origin. For example, as you’re checking
your bank account in one tab, you could have the evil.com website open
in another tab. The scripts from evil.com should not be able to make
AJAX requests to your bank API (e.g., withdrawing money from your
account!) using your credentials.
No, see https://spring.io/guides/gs/rest-service-cors/:
So that the RESTful web service will include CORS access control
headers in its response, you just have to add a #CrossOrigin
annotation to the handler method:
#CrossOrigin(origins = "http://localhost:9000")
#GetMapping("/greeting")
public Greeting greeting(#RequestParam(required=false, defaultValue="World") String name) {
System.out.println("==== in greeting ====");
return new Greeting(counter.incrementAndGet(), String.format(template, name));
}
CORS support is disabled by default and is only enabled once the endpoints.cors.allowedorigins
property has been set. The configuration below permits GET and POST calls from the
example.com domain:
endpoints.cors.allowed-origins=http://example.com
endpoints.cors.allowed-methods=GET,POST
Spring boot ref guide.
Check EndpointCorsProperties for a complete list of options.
No. The scripts from evil.com should not be able to make AJAX requests to your bank, for example
I have the following code that handles a CORS request
#CrossOrigin(origins = "*", methods = RequestMethod.PUT)
#RequestMapping(value = "/componentOrder", method = RequestMethod.PUT)
public #ResponseBody List<Map<String, Long>> syncComponentOrder(#PathVariable Long id,
#RequestBody List<Map<String, Long>> orders) { ... }
Even though Spring documentation says that this should handle a CORS request correctly but my Chrome still reports an error:
I need some help finding out why, the other CORS endpoints work perfectly.
Figured out why, this is not because the #CrossOrigin annotation, but rather I was missing a path parameter in my request, that's why I saw internal server error.
Strangely no error/warning is printed out, making it hard to debug.