Spring Security enables developers to build secure Spring applications. Get started with our top 10 Spring Security best practices.
If you’ve reached this page, you’re probably familiar with Spring and its basic mechanisms already. From its inception in 2002, Spring has become one of the dominant frameworks to build any kind of web application in Java.
Web applications usually are the biggest interface between a company and its users—both internal and external. When security is neglected at the developer level, applications can become very desirable targets to hackers. A successful exploitation of a vulnerable application can expose sensitive customer and company data, result in monetary losses, and cause permanent damage to a company’s reputation. Spring introduces several security mechanisms that can help you avoid these pitfalls. Let’s look at 10 Spring Security best practices you should follow to keep your application secure.
This is really an opening to most of the coming tips. As boring as it may sound, securing an application is mostly about plugging all the holes an attacker could sneak through. Such a job is much better handled by a tested and proven security library than by anything a single project could add to its own developments.
For Spring applications, Spring Security is that tested and proven library. It simplifies the handling of authentication, puts in place a set of protections against exploits such as CSRF, and helps you set the right configuration to protect against many other threats.
Adding Spring Security is as simple as adding a few dependencies, but of course, to get the full value out of it, you also need to do a bit of extra work. This is the topic of the next tips.
HTTP security headers can be useful to help your application prevent malicious activity. Those headers need to be inserted in the HTTP response sent back by the server, and they define some of the browser’s behaviors, such as boundaries to what the client code is allowed to do in the browser, or policies related to transport security, referrer indication or HTML frames.
With Spring Security, some of those headers are initially set to the most secure option, while others need to be fine-tuned into the best combination for your application and the required security level. When defining the security configuration, headers can be configured through HttpSecurity.headers() methods.
Web applications can be subject to server-side injection attacks, in which the hacker injects a malicious command into data he provides to the application. Those attacks are made possible if the application constructs an execution command (such as a SQL query) with the untrusted data provided by the user. In this situation, for the hacker, it’s just a matter of finding the right sequence of characters to corrupt the constructed command into performing their own nefarious intent.
There are multiple ways that Spring can help you avoid mixing data with commands. A very powerful way to do this for database access is to use Spring Data JPA. By abstracting most database accesses from your application, it reduces the risk of having handwritten SQL queries that could be misused.
In some cases, it is not possible to use such data abstraction and you may have to rely on actual SQL queries. In those situations, the best solution is to use query parametrization.
Sanitization can be achieved in different ways:
In order to isolate potentially malicious scripts, same origin policy (SOP) prevents data coming from one origin to be read or executed by a script from another origin, and it also prevents some requests to be performed to another origin, when using specific HTTP methods or headers. For example, if you are browsing a site from one origin, your browser will not execute code contained in a response to a request to another origin. It also means that your scripts cannot perform a request to another origin, if those requests are not simple GET, HEAD, or POST requests with standard headers. By “origin” here, we mean a combination of protocol, host name, and port.
Strict SOP can be too restrictive for big web applications, which is why cross origin resource sharing (CORS) exists. CORS allows the server to relax the SOP restrictions and allow cross origin request and/or response reading and execution for specific caller origins and called endpoints. One important aspect to remember is that, although CORS rules are defined by the server, it is the browser’s responsibility to comply with them.
Spring provides easy ways to define your CORS policy. You can set them up centrally in a WebMvcConfigurer class, or through a @CrossOrigin annotation for a @Controller or @RequestMapping.
As a general advice, make sure you understand the way SOP and CORS work. They protect your users and your data only in conjunction with the browser. CORS can also have a very fine granularity—make sure you only allow the endpoints that need it, from the origins that need it.
Most web applications need to decide which resource should be given access by the current user. Rules defining who can do what are defined as the authorization rules, and one key element they work with is the knowledge of who the user is, which is handled by authentication. A failure in authentication security can compromise the whole application, so it really is key to secure this as much as possible.
Spring Security simplifies authentication and helps you make it more secure. By default, it includes a form-based login page which verifies username and password provided by the user against the application users database, and then keeps track of the user authentication during their session. This behavior is configurable by extending the WebSecurityConfigurerAdapter class, allowing you to define how your users get authenticated and other aspects such as the presence of a “remember me” feature.
One key aspect to consider is the storage of passwords. If you store your users’ password hashes in your application database, you must assume that it is possible for a malicious actor to gain access to that database and use it to perform an offline brute-force attack on the passwords. One way to protect against that is by putting in place a one-way password encoding algorithm which is too complex for such a brute-force attack to be quickly completed. The current recommendation is to use bcrypt with a cost factor of 13. With Spring, you can do this by defining an implementation for passwordEncoder() interface.
The previous Spring Security best practices described a few reasons why managing your own authentication is a challenge. Additional complexities arise when you try and provide advanced mechanisms beyond the very basic, such as password policies, enumeration attack protection, or multi-factor authentication (MFA) features.
The recommended solution is to delegate the authentication of your users through OpenID Connect (OIDC), either to public providers (such as Google or GitHub) or to a private provider. From version 5.1, Spring offers built-in support to integrate OIDC-based authentication. You will need to provide the configuration settings of your provider in the application and call the oauth2Login method to set your application to request that kind of login.
Once you have a robust authentication mechanism, your application will always know who is using it. To know if that user is entitled to access a resource in your application, you need the right authorization mechanism.
Spring offers a customizable authorization system that can be used to implement role-based-access-control (RBAC) and other methods such attribute-based-access-control (ABAC). Users can have roles and authorities. Authorization can be granted to a whole application, to specific endpoints, and even to individual operations. It is also possible to implement your own custom permission check methods.
It is important to understand the authorization model needed by your application and to enforce defense in-depth by making authorization checks at all levels of the application (application, endpoint, and operation).
Every application must handle secrets—database credentials, cryptographic keys, or other sensitive data that would allow an attacker to impersonate the application. When those secrets are provided in a configuration file, they may be accessed by the wrong person in many ways: through a directory traversal attack, because a developer publishes the file by mistake in a public repository, or even by a malicious insider.
Using a dedicated secrets management system, such as HashiCorp’s Vault, ensures teams are following Spring security best practices. The application secrets are stored in the vault and are provided only to authorized clients. Getting your secrets accidentally revealed to the world becomes a much smaller risk.
Spring also provides seamless integration with such a vault, through Spring Vault. Using this library makes the secret management very simple to the developer.
With the previous tips, we’ve covered aspects of security which are more prevalent in web development. It is important to remember, though, that all other security aspects should be studied as well. It’s important, as for any other application, to check that libraries used as dependencies don’t bring vulnerability risk with them, which implies that you stay up to date in the versions of your libraries. It’s also important to identify abuse cases, perform adapted security testing, and review your code. Tooling can help with some of those aspects, but a proper application security process is really the key to reducing the security risk of your application.
Spring Security brings powerful mechanisms to help you with security and it is important to use them properly. In this post, we’ve only touched the surface of what you can do to make your Spring application more secure. An additional tip would be to invest some of your time into educating your team about what makes an application safer and what pitfalls should be avoided. Our eLearning material provide deeper insight on those topics, not just for Spring applications, but also for many other aspects of application security.
Hugues Martin is a Sales Engineer at Synopsys Integrity Group, where he helps customers choose the best solutions for their AppSec initiatives. Before joining Synopsys in 2020, Hugues has spent more than 20 years immersed in the software development world, as a developer, architect, sales engineer, trainer, and technical product manager, for applications ranging from .com era eCommerce web sites to modern day collaboration platforms. He enjoys learning, putting new skills into practice, and sharing his own knowledge.