In the previous chapter, you learned how fake HTTP requests could be sent when a hacker steals a user’s session. Now you will see how limiting accepted domain names with the right CORS configuration can prevent hackers from redirecting your web application users to malicious sites.
Understanding CORS Attacks
Another common OWASP researched web application attack is a malicious browser redirect. Web applications often have HTTP GET
or POST
requests that can include a link to a URL that leads a user to another website.
For example, an OAuth 2.0 login button would lead you to Facebook or Google, which is a legitimate part of your OAuth 2.0 workflow. You don’t need to add that to Spring Security’s CORs protection. However, if you can lead users to Facebook or Google, a hacker can lead them to a malicious host that can download adware or malware!
This attack is called a browser redirect attack and it is prevented by a default browser safeguards. However, web applications need to redirect users to external websites, so they use CORS (Cross-origin resource sharing).
Spring Security blocks CORS attacks by default by preventing an HTTP request to a URL destination that is different from the origin (the host and port). So if an HTTP request has a destination to a different port and same host, that is a different origin. Both the host (the server that the domain is on) and the port must be the same for it to be considered the same origin.
CORS protection on your Spring web application begins before Spring Security even starts. That’s because, in addition to requiring the same origin, a CORS cookie or authorization token is required in the header of the initial HTTP OPTIONS request after authorization. After that, all requests that include this cookie or token are allowed. If a user sends a request and isn’t authorized in a session, the header reflects that.
Here are two HTTP header messages that determine if a request is allowed or blocked:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin
Configure CORS Protection in Spring Security
CORS protection is automatically enabled; however, if you are creating a custom security filter chain, you need to know to add it. When you custom configure specific settings for CORS, you can choose what types of HTTP methods to allow, including HEAD
, PUT
, GET
, POST
, OPTIONS
, DELETE
, CONNECT
, and TRACE
.
Step 1
If you need to set up a custom configuration, manually enable CORS by adding the cors()
filter to your security filter chain before coding in your custom configurations.
@Override
protected void configure(HttpSecurity http)throws Exception{
http.cors();
}
The cors()
default filter bypasses an authorization check on OPTIONS requests. The initial HTTP request made cors()
is a resource check to use the Spring framework, but requests don’t send credentials or URLs, so they don’t require authorization. They provide information about what resources are available at a given URL.
Step 2
There are two ways to go about this:
Use the WebMvcConfigurer class to set up a global CORS configuration for the entire web application with custom configurations.
Use the
@CrossOrigin
annotation to your controller for custom configurations.
It is hard to say which system will work best for you. You can use either/or a combination of the two to fit your configuration style. I suggest experimenting with both options to see what works best for your web application and security requirements.
Option 1: In Your Spring Security Configuration (Manually Enable CORS)
In your WebMvcConfigurer class, create a configuration class with CorsRegistry. You are enabling a global registration to use CORS. However, alone, this provides incomplete coverage:
@Configuration
public class WebConfiguration implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
}
This code says you will use CORS for everything using the default settings. It overrides Spring Security’s safeguard against CORS attacks. When you manually enable CORS globally, you are allowing GET, HEAD, and POST methods from all origins. 😱 The addMapping(“/**”) means it works from the root of the web application, hence globally, so the entire web application is unprotected from CORS attacks. ☠️ Leaving it like this is unsafe because it disables any safeguards against CORS attacks.
With this, you can allow certain methods. For example, if you want to allow all HTTP request methods, use the configuration below:
@Configuration
public class WebConfiguration implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedMethods("*");
}
}
To add custom configurations, specify your requirements in the registry.addMapping() method. The current code is unsafe because it disables any safeguards against CORS attacks, and allows all HTTPS requests. If you use this, make sure you have another safeguard to prevent browser redirects.
As a best practice for Spring web applications, the below configuration allows you to use CORS to allow specific domains and necessary HTTP request methods. Spring Security still applies its anti-CORS attack protection, and won't allow your web application to redirect users to other rogue sites.
@Configuration
public class WebConfiguration implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("https://cheezburger.com")
.allowedMethods("GET", "POST")
.allowCredentials(false).maxAge(3600);
}
}
allowedOrigins()
allows you to specify allowed origin domains.allowedMethods()
allows you to specify the allowed HTTP request methods.allowCredentials()
is set to false to ensure that user credential information is not sent in the token or cookie.
Option 2: In Your Controller
A custom configuration can be done without using the WebMvcConfigurer class. Instead, you can use the @CrossOrigin
annotation to work with the entire controller (global) or with individual methods.
If you go to your controller configuration file, you add the @CrossOrigin
annotation above your class declaration. You can add parameters to it as part of the configuration.
Here is an example of a CORS ruleset for the methods in the controller class:
@CrossOrigin(http://localhost:8080)
@RestController
public class LoginController
{
@RolesAllowed({"USER"})
@RequestMapping("/**")
public String getUser()
{
return "Welcome User";
}
@RolesAllowed("ADMIN","USER")
@RequestMapping("/admin")
public String getAdmin()
{
return "Welcome Admin";
}
}
Essentially, the HTTP requests for the getUser
and getAdmin
methods have to come from the origin http://localhost:8080. I’m using http://localhost:8080 to be safe because it is what our origin is set to if we are creating our practice web app locally and connecting to the local web server.
Don't specify @CrossOrigin
configuration at the global level by placing the annotation above the class without anything after it. It allows any origin to any method in your controller. This is bad! But sometimes you want to allow all origins to a certain method, so in the example below, I set the @CrossOrigin
annotation at default, which allows the HTTP requests to the getUser method to come from any origin with any type of HTTP request method.
@RestController
public class LoginController
{
@RolesAllowed({"USER","ADMIN"})
@RequestMapping("/admin")
public String getAdmin()
{
return "Welcome Admin";
}
@CrossOrigin
@RolesAllowed("USER")
@RequestMapping("/*")
public String getUser()
{
return "Welcome User";
}
}
Now I want to add my recommended route for using the @CrossOrigin
annotation. I’m going to give an example to illustrate what I’m saying. As a best practice, you want to use the @CrossOrigin
based on your test methods with a specific URL and time limit (in seconds) as follows:
@CrossOrigin(origins = www.allmyusers.com, maxAge = 3600)
@RolesAllowed("USER")
@RequestMapping("/*")
public String getUser()
{
return "Welcome User";
}
Phew!
Now, you've learned quite a bit about many ways to customize your cors() settings to allow specific cross-origin requests for your web app. Don't take the easy way out and open everything up. It opens you up to trouble.
Let's Recap!
CORS attacks happen through browser redirects to malicious sites.
You can use Spring Security
cors()
customizations to limit external hosts by specifying them in your code.There are two ways to program in which domain origins are allowed after you enable
cors()
in the filter chain:The @CrossOrigin annotation in the controller.
The WebMvcConfigurer class to create specific mappings.
Cors() customizations can be made to permit only certain domains, ports, and HTTP methods. You can also create an expiration time.
Let's look at how you can secure your sessions in the next chapter!