Spring Security in Action
()
About this ebook
Summary
While creating secure applications is critically important, it can also be tedious and time-consuming to stitch together the required collection of tools. For Java developers, the powerful Spring Security framework makes it easy for you to bake security into your software from the very beginning. Filled with code samples and practical examples, Spring Security in Action teaches you how to secure your apps from the most common threats, ranging from injection attacks to lackluster monitoring. In it, you'll learn how to manage system users, configure secure endpoints, and use OAuth2 and OpenID Connect for authentication and authorization.
Purchase of the print book includes a free eBook in PDF, Kindle, and ePub formats from Manning Publications.
About the technology
Security is non-negotiable. You rely on Spring applications to transmit data, verify credentials, and prevent attacks. Adopting "secure by design" principles will protect your network from data theft and unauthorized intrusions.
About the book
Spring Security in Action shows you how to prevent cross-site scripting and request forgery attacks before they do damage. You’ll start with the basics, simulating password upgrades and adding multiple types of authorization. As your skills grow, you'll adapt Spring Security to new architectures and create advanced OAuth2 configurations. By the time you're done, you'll have a customized Spring Security configuration that protects against threats both common and extraordinary.
What's inside
Encoding passwords and authenticating users
Securing endpoints
Automating security testing
Setting up a standalone authorization server
About the reader
For experienced Java and Spring developers.
About the author
Laurentiu Spilca is a dedicated development lead and trainer at Endava, with over ten years of Java experience.
Table of Contents
PART 1 - FIRST STEPS
1 Security Today
2 Hello Spring Security
PART 2 - IMPLEMENTATION
3 Managing users
4 Dealing with passwords
5 Implementing authentication
6 Hands-on: A small secured web application
7 Configuring authorization: Restricting access
8 Configuring authorization: Applying restrictions
9 Implementing filters
10 Applying CSRF protection and CORS
11 Hands-on: A separation of responsibilities
12 How does OAuth 2 work?
13 OAuth 2: Implementing the authorization server
14 OAuth 2: Implementing the resource server
15 OAuth 2: Using JWT and cryptographic signatures
16 Global method security: Pre- and postauthorizations
17 Global method security: Pre- and postfiltering
18 Hands-on: An OAuth 2 application
19 Spring Security for reactive apps
20 Spring Security testing
Laurentiu Spilca
Laurentiu Spilca is a skilled Java and Spring developer and an experienced technology instructor. He is the author of Manning’s Spring Start Here and Spring Security in Action.
Read more from Laurentiu Spilca
Spring Start Here: Learn what you need and learn it well Rating: 0 out of 5 stars0 ratingsSpring Security in Action, Second Edition Rating: 0 out of 5 stars0 ratingsTroubleshooting Java: Read, debug, and optimize JVM applications Rating: 0 out of 5 stars0 ratings
Related to Spring Security in Action
Related ebooks
Spring Microservices in Action Rating: 0 out of 5 stars0 ratingsSpring in Action, Sixth Edition Rating: 5 out of 5 stars5/5Microservices Security in Action Rating: 0 out of 5 stars0 ratingsKubernetes Native Microservices with Quarkus and MicroProfile Rating: 0 out of 5 stars0 ratingsAPI Security in Action Rating: 5 out of 5 stars5/5Bootstrapping Microservices with Docker, Kubernetes, and Terraform: A project-based guide Rating: 3 out of 5 stars3/5Microservices in .NET, Second Edition Rating: 0 out of 5 stars0 ratingsExpress in Action: Writing, building, and testing Node.js applications Rating: 4 out of 5 stars4/5Cloud Native Patterns: Designing change-tolerant software Rating: 4 out of 5 stars4/5OAuth 2 in Action Rating: 0 out of 5 stars0 ratingsIstio in Action Rating: 0 out of 5 stars0 ratingsASP.NET Core Security Rating: 5 out of 5 stars5/5Spring in Action Rating: 4 out of 5 stars4/5Testing Java Microservices: Using Arquillian, Hoverfly, AssertJ, JUnit, Selenium, and Mockito Rating: 0 out of 5 stars0 ratingsSpring Boot in Action Rating: 0 out of 5 stars0 ratingsMicroservices in Action Rating: 0 out of 5 stars0 ratingsModern Java in Action: Lambdas, streams, functional and reactive programming Rating: 2 out of 5 stars2/5Docker in Action, Second Edition Rating: 3 out of 5 stars3/5Angular Development with TypeScript Rating: 0 out of 5 stars0 ratingsJUnit in Action Rating: 0 out of 5 stars0 ratingsKubernetes in Action Rating: 0 out of 5 stars0 ratingsSpring Boot Cookbook Rating: 0 out of 5 stars0 ratingsAPI Design Patterns Rating: 5 out of 5 stars5/5Seriously Good Software: Code that works, survives, and wins Rating: 5 out of 5 stars5/5Micro Frontends in Action Rating: 0 out of 5 stars0 ratingsReact Quickly: Painless web apps with React, JSX, Redux, and GraphQL Rating: 0 out of 5 stars0 ratingsThe Design of Web APIs Rating: 0 out of 5 stars0 ratingsTypeScript Quickly Rating: 0 out of 5 stars0 ratingsGraphQL in Action Rating: 2 out of 5 stars2/5Angular in Action Rating: 0 out of 5 stars0 ratings
Internet & Web For You
The $1,000,000 Web Designer Guide: A Practical Guide for Wealth and Freedom as an Online Freelancer Rating: 4 out of 5 stars4/5Get Into UX: A foolproof guide to getting your first user experience job Rating: 4 out of 5 stars4/5Notion for Beginners: Notion for Work, Play, and Productivity Rating: 4 out of 5 stars4/5Everybody Lies: Big Data, New Data, and What the Internet Can Tell Us About Who We Really Are Rating: 4 out of 5 stars4/5Python: Learn Python in 24 Hours Rating: 4 out of 5 stars4/5Coding All-in-One For Dummies Rating: 4 out of 5 stars4/5Grokking Algorithms: An illustrated guide for programmers and other curious people Rating: 4 out of 5 stars4/5The Basics of User Experience Design by Interaction Design Foundation Rating: 4 out of 5 stars4/5Coding For Dummies Rating: 5 out of 5 stars5/5Coding with AI For Dummies Rating: 0 out of 5 stars0 ratingsSocial Engineering: The Science of Human Hacking Rating: 3 out of 5 stars3/5The Logo Brainstorm Book: A Comprehensive Guide for Exploring Design Directions Rating: 4 out of 5 stars4/5The Digital Marketing Handbook: A Step-By-Step Guide to Creating Websites That Sell Rating: 5 out of 5 stars5/5The Designer’s Guide to Figma: Master Prototyping, Collaboration, Handoff, and Workflow Rating: 0 out of 5 stars0 ratingsStop Asking Questions: How to Lead High-Impact Interviews and Learn Anything from Anyone Rating: 5 out of 5 stars5/5Get Started in UX: The Complete Guide to Launching a Career in User Experience Design Rating: 4 out of 5 stars4/5Principles of Web Design Rating: 0 out of 5 stars0 ratingsUX/UI Design Playbook Rating: 4 out of 5 stars4/5Learn JavaScript in 24 Hours Rating: 3 out of 5 stars3/52022 Adobe® Premiere Pro Guide For Filmmakers and YouTubers Rating: 5 out of 5 stars5/5LinkedIn Profile Optimization For Dummies Rating: 4 out of 5 stars4/5RESTful API Design - Best Practices in API Design with REST: API-University Series, #3 Rating: 5 out of 5 stars5/5HTML, CSS, & JavaScript All-in-One For Dummies Rating: 0 out of 5 stars0 ratingsWeb Design For Dummies Rating: 4 out of 5 stars4/5Learn NodeJS in 1 Day: Complete Node JS Guide with Examples Rating: 3 out of 5 stars3/5Six Figure Blogging Blueprint Rating: 5 out of 5 stars5/5Making Money By Selling 3D Models Online Rating: 5 out of 5 stars5/5
Reviews for Spring Security in Action
0 ratings0 reviews
Book preview
Spring Security in Action - Laurentiu Spilca
Part 1. First Steps
Security is one of the essential nonfunctional qualities of a software system. One of the most crucial aspects you learn in this book is that you should consider security from the beginning stages of application development. In chapter 1, we start by discussing the place of security in the development process of an application. Then, in chapter 2, I introduce you to the basic components of Spring Security’s backbone architecture by implementing a few straightforward projects.
The purpose of this part is to get you started with Spring Security, especially if you are just beginning to learn this framework. However, even if you already know some aspects of application-level security and the underlying architecture of Spring Security, I recommend you read this part as a refresher.
1 Security today
This chapter covers
What Spring Security is and what you can solve by using it
What security is for a software application
Why software security is essential and why you should care
Common vulnerabilities that you’ll encounter at the application level
Today, more and more developers are becoming aware of security. It’s not, unfortunately, a common practice to take responsibility for security from the beginning of the development of a software application. This attitude should change, and everyone involved in developing a software system must learn to consider security from the start!
Generally, as developers, we begin by learning that the purpose of an application is to solve business issues. This purpose refers to something where data could be processed somehow, persisted, and eventually displayed to the user in a specific way as specified by some requirements. This overview of software development, which is somehow imposed from the early stages of learning these techniques, has the unfortunate disadvantage of hiding practices that are also part of the process. While the application works correctly from the user’s perspective and, in the end, it does what the user expects in terms of functionality, there are lots of aspects hidden in the final result.
Nonfunctional software qualities such as performance, scalability, availability, and, of course, security, as well as others, can have an impact over time, from short to long term. If not taken into consideration early on, these qualities can dramatically affect the profitability of the application owners. Moreover, the neglect of these considerations can also trigger failures in other systems as well (for example, by the unwilling participation in a distributed denial of service (DDoS) attack). The hidden aspects of nonfunctional requirements (the fact that it’s much more challenging to see if something’s missing or incomplete) makes these, however, more dangerous.
Figure 1.1 A user mainly thinks about functional requirements. Sometimes, you might see them aware of performance, which is nonfunctional, but unfortunately, it’s quite unusual that a user cares about security. Nonfunctional requirements tend to be more transparent than functional ones.
There are multiple nonfunctional aspects to consider when working on a software system. In practice, all of these are important and need to be treated responsibly in the process of software development. In this book, we focus on one of these: security. You’ll learn how to protect your application, step by step, using Spring Security.
But before starting, I’d like to make you aware of the following: depending on how much experience you have, you might find this chapter cumbersome. Don’t worry too much if you don’t understand absolutely all the aspects for now. In this chapter, I want to show you the big picture of security-related concepts. Throughout the book, we work on practical examples, and where appropriate, I’ll refer back to the description I give in this chapter. Where applicable, I’ll also provide you with more details. Here and there, you’ll find references to other materials (books, articles, documentation) on specific subjects that are useful for further reading.
1.1 Spring Security: The what and the why
In this section, we discuss the relationship between Spring Security and Spring. It is important, first of all, to understand the link between the two before starting to use those. If we go to the official website, https://spring.io/projects/spring-security, we see Spring Security described as a powerful and highly customizable framework for authentication and access control. I would simply say it is a framework that enormously simplifies applying (or baking
) security for Spring applications.
Spring Security is the primary choice for implementing application-level security in Spring applications. Generally, its purpose is to offer you a highly customizable way of implementing authentication, authorization, and protection against common attacks. Spring Security is an open source software released under the Apache 2.0 license. You can access its source code on GitHub at https://github.com/spring-projects/ spring-security/. I highly recommend that you contribute to the project as well.
Note You can use Spring Security for both standard web servlets and reactive applications. To use it, you need at least Java 8, although the examples in this book use Java 11, which is the latest long-term supported version.
I can guess that if you opened this book, you work on Spring applications, and you are interested in securing those. Spring Security is most likely the best choice for you. It’s the de facto solution for implementing application-level security for Spring applications. Spring Security, however, doesn’t automatically secure your application. It’s not some kind of magic panacea that guarantees a vulnerability-free app. Developers need to understand how to configure and customize Spring Security around the needs of their applications. How to do this depends on many factors, from the functional requirements to the architecture.
Technically, applying security with Spring Security in Spring applications is simple. You’ve already implemented Spring applications, so you know that the framework’s philosophy starts with the management of the Spring context. You define beans in the Spring context to allow the framework to manage these based on the configurations you specify. And you use only annotations to make these configurations and leave behind the old-fashioned XML configuration style!
You use annotations to tell Spring what to do: expose endpoints, wrap methods in transactions, intercept methods in aspects, and so on. The same is true with Spring Security configurations, which is where Spring Security comes into play. What you want is to use annotations, beans, and in general, a Spring-fashioned configuration style comfortably when defining your application-level security. In a Spring application, the behavior that you need to protect is defined by methods.
To think about application-level security, you can consider your home and the way you allow access to it. Do you place the key under the entrance rug? Do you even have a key for your front door? The same concept applies to applications, and Spring Security helps you develop this functionality. It’s a puzzle that offers plenty of choices for building the exact image that describes your system. You can choose to leave your house completely unsecured, or you can decide not to allow everyone to enter your home.
The way you configure security can be straightforward like hiding your key under the rug, or it can be more complicated like choosing a variety of alarm systems, video cameras, and locks. In your applications, you have the same options, but as in real life, the more complexity you add, the more expensive it gets. In an application, this cost refers to the way security affects maintainability and performance.
But how do you use Spring Security with Spring applications? Generally, at the application level, one of the most encountered use cases is when you’re deciding whether someone is allowed to perform an action or use some piece of data. Based on configurations, you write Spring Security components that intercept the requests and that ensure whoever makes the requests has permission to access protected resources. The developer configures components to do precisely what’s desired. If you mount an alarm system, it’s you who should make sure it’s also set up for the windows as well as for the doors. If you forget to set it up for the windows, it’s not the fault of the alarm system that it doesn’t trigger when someone forces a window.
Other responsibilities of Spring Security components relate to data storage as well as data transit between different parts of the systems. By intercepting calls to these different parts, the components can act on the data. For example, when data is stored, these components can apply encryption or hashing algorithms. The data encodings keep the data accessible only to privileged entities. In a Spring application, the developer has to add and configure a component to do this part of the job wherever it’s needed. Spring Security provides us with a contract through which we know what the framework requires to be implemented, and we write the implementation according to the design of the application. We can say the same thing about transiting data.
In real-world implementations, you’ll find cases in which two communicating components don’t trust each other. How can the first know that the second one sent a specific message and it wasn’t someone else? Imagine you have a phone call with somebody to whom you have to give private information. How do you make sure that on the other end is indeed a valid individual with the right to get that data, and not somebody else? For your application, this situation applies as well. Spring Security provides components that allow you to solve these issues in several ways, but you have to know which part to configure and then set it up in your system. This way, Spring Security intercepts messages and makes sure to validate communication before the application uses any kind of data sent or received.
Like any framework, one of the primary purposes of Spring is to allow you to write less code to implement the desired functionality. And this is also what Spring Security does. It completes Spring as a framework by helping you write less code to perform one of the most critical aspects of an application--security. Spring Security provides predefined functionality to help you avoid writing boilerplate code or repeatedly writing the same logic from app to app. But it also allows you to configure any of its components, thus providing great flexibility. To briefly recap this discussion:
You use Spring Security to bake application-level security into your applications in the Spring
way. By this, I mean, you use annotations, beans, the Spring Expression Language (SpEL), and so on.
Spring Security is a framework that lets you build application-level security. However, it is up to you, the developer, to understand and use Spring Security properly. Spring Security, by itself, does not secure an application or sensitive data at rest or in flight.
This book provides you with the information you need to effectively use Spring Security.
Alternatives to Spring Security
This book is about Spring Security, but as with any solution, I always prefer to have a broad overview. Never forget to learn the alternatives that you have for any option. One of the things I’ve learned over time is that there’s no general right or wrong. Everything is relative
also applies here!
You won’t find a lot of alternatives to Spring Security when it comes to securing a Spring application. One alternative you could consider is Apache Shiro (https://shiro.apache.org). It offers flexibility in configuration and is easy to integrate with Spring and Spring Boot applications. Apache Shiro sometimes makes a good alternative to the Spring Security approach.
If you’ve already worked with Spring Security, you’ll find using Apache Shiro easy and comfortable to learn. It offers its own annotations and design for web applications based on HTTP filters, which greatly simplify working with web applications. Also, you can secure more than just web applications with Shiro, from smaller command-line and mobile applications to large-scale enterprise applications. And even if simple, it’s powerful enough to use for a wide range of things from authentication and authorization to cryptography and session management.
However, Apache Shiro could be too light
for the needs of your application. Spring Security is not just a hammer, but an entire set of tools. It offers a larger scale of possibilities and is designed specifically for Spring applications. Moreover, it benefits from a larger community of active developers, and it is continuously enhanced.
1.2 What is software security?
Software systems today manage large amounts of data, of which a significant part can be considered sensitive, especially given the current General Data Protection Regulations (GDPR) requirements. Any information that you, as a user, consider private is sensitive for your software application. Sensitive data can include harmless information like a phone number, email address, or identification number; although, we generally think more about data that is riskier to lose, like your credit card details. The application should ensure that there’s no chance for that information to be accessed, changed, or intercepted. No parties other than the users to whom this data is intended should be able to interact in any way with it. Broadly expressed, this is the meaning of security.
NOTE GDPR created a lot of buzz globally after its introduction in 2018. It generally represents a set of European laws that refer to data protection and gives people more control over their private data. GDPR applies to the owners of systems having users in Europe. The owners of such applications risk significant penalties if they don’t respect the regulations imposed.
We apply security in layers, with each layer requiring a different approach. Compare these layers to a protected castle (figure 1.2). A hacker needs to bypass several obstacles to obtain the resources managed by the app. The better you secure each layer, the lower the chance an individual with bad intentions manages to access data or perform unauthorized operations.
Figure 1.2 The Dark Wizard (a hacker) has to bypass multiple obstacles (security layers) to steal the Magic Sword (user resources) from the Princess (your application).
Security is a complex subject. In the case of a software system, security doesn’t apply only at the application level. For example, for networking, there are issues to be taken into consideration and specific practices to be used, while for storage, it’s another discussion altogether. Similarly, there’s a different philosophy in terms of deployment, and so on. Spring Security is a framework that belongs to application-level security. In this section, you’ll get a general picture of this security level and its implications.
Application-level security (figure 1.3) refers to everything that an application should do to protect the environment it executes in, as well as the data it processes and stores. Mind that this isn’t only about the data affected and used by the application. An application might contain vulnerabilities that allow a malicious individual to affect the entire system!
Figure 1.3 We apply security in layers, and each layer depends on those below it. In this book, we discuss Spring Security, which is a framework used to implement application-level security at the top-most level.
To be more explicit, let’s discuss using some practical cases. We’ll consider a situation in which we deploy a system as in figure 1.4. This situation is common for a system designed using a microservices architecture, especially if you deploy it in multiple availability zones in the cloud.
Figure 1.4 If a malicious user manages to get access to the virtual machine (VM) and there’s no applied application-level security, a hacker can gain control of the other applications in the system. If communication is done between two different availability zones (AZ), a malicious individual will find it easier to intercept the messages. This vulnerability allows them to steal data or to impersonate users.
With such microservice architectures, we can encounter various vulnerabilities, so you should exercise caution. As mentioned earlier, security is a cross-cutting concern that we design on multiple layers. It’s a best practice when addressing the security concerns of one of the layers to assume as much as possible that the above layer doesn’t exist. Think about the analogy with the castle in figure 1.2. If you manage the layer
with 30 soldiers, you want to prepare them to be as strong as possible. And you do this even knowing that before reaching them, one would need to cross the fiery bridge.
With this in mind, let’s consider that an individual driven by bad intentions would be able to log in to the virtual machine (VM) that’s hosting the first application. Let’s also assume that the second application doesn’t validate the requests sent by the first application. The attacker can then exploit this vulnerability and control the second application by impersonating the first one.
Also, consider that we deploy the two services to two different locations. Then the attacker doesn’t need to log in to one of the VMs as they can directly act in the middle of communications between the two applications.
NOTE An availability zone (AZ in figure 1.4) in terms of cloud deployment is a separate data center. This data center is situated far enough geographically (and has other dependencies) from other data centers of the same region that, if one availability zone fails, the probability that others are failing too is minimal. In terms of security, an important aspect is that traffic between two different data centers generally goes across a public network.
Monolithic and microservices
The discussion on monolithic and microservices architectural styles is a whole different tome. I refer to these in multiple places in this book, so you should at least be aware of the terminology. For an excellent discussion of the two architectural styles, I recommend that you read Chris Richardson’s Microservices Patterns (Manning, 2018).
By monolithic architecture, we refer to an application in which we implement all the responsibilities in the same executable artifact. Consider this as one application that fulfills all use cases. The responsibilities can sometimes be implemented within different modules to make the application more comfortable to maintain. But you can’t separate the logic of one from the logic of others at runtime. Generally, monolithic architectures offer less flexibility for scaling and deployment management.
With a microservices system, we implement the responsibilities within different executable artifacts. You can see the system as being formed of multiple applications that execute at the same time and communicate between themselves when needed via the network. While this offers more flexibility for scaling, it introduces other difficulties. We can enumerate here latencies, security concerns, network reliability, distributed persistence, and deployment management.
I referred earlier to authentication and authorization. And, indeed, these are often present in most applications. Through authentication, an application identifies a user (a person or another application). The purpose of identifying these is to be able to decide afterward what they should be allowed to do--that’s authorization. I provide quite a lot of details on authentication and authorization, starting with chapter 3 and continuing throughout the book.
In an application, you often find the need to implement authorization in different scenarios. Consider another situation: most applications have restrictions regarding the user for obtaining access certain functionality. Achieving this implies first the need to identify who creates an access to request for a specific feature--that’s authentication. As well, we need to know their privileges to allow the user to use that part of the system. As the system becomes more complex, you’ll find different situations that require a specific implementation related to authentication and authorization.
For example, what if you’d like to authorize a particular component of the system against a subset of data or operations on behalf of the user? Let’s say the printer needs access to read the user’s documents. Should you simply share the credentials of the user with the printer? But that allows the printer more rights than needed! And it also exposes the credentials of the user. Is there a proper way to do this without impersonating the user? These are essential questions, and the kind of questions you encounter when developing applications: questions that we not only want to answer, but for which you’ll see applications with Spring Security in this book.
Depending on the chosen architecture for the system, you’ll find authentication and authorization at the level of the entire system, as well as for any of the components. And as you’ll see further along in this book, with Spring Security, you’ll sometimes prefer to use authorization even for different tiers of the same component. In chapter 16, we’ll discuss more on global method security, which refers to this aspect. The design gets even more complicated when you have a predefined set of roles and authorities.
I would also like to bring to your attention data storage. Data at rest adds to the responsibility of the application. Your app shouldn’t store all its data in a readable format. The application sometimes needs to keep the data either encrypted with a private key or hashed. Secrets like credentials and private keys can also be considered data at rest. These should be carefully stored, usually in a secrets vault.
NOTE We classify data as at rest
or in transition.
In this context, data at rest refers to data in computer storage or, in other words, persisted data. Data in transition applies to all the data that’s exchanged from one point to another. Different security measures should, therefore, be enforced, depending on the type of data.
Finally, an executing application must manage its internal memory as well. It may sound strange, but data stored in the heap of the application can also present vulnerabilities. Sometimes the class design allows the app to store sensitive data like credentials or private keys for a long time. In such cases, someone who has the privilege to make a heap dump could find these details and then use them maliciously.
With a short description of these cases, I hope I’ve managed to provide you with an overview of what we mean by application security, as well as the complexity of this subject. Software security is a tangled subject. One who is willing to become an expert in this field would need to understand (as well as to apply) and then test solutions for all the layers that collaborate within a system. In this book, however, we’ll focus only on presenting all the details of what you specifically need to understand in terms of Spring Security. You’ll find out where this framework applies and where it doesn’t, how it helps, and why you should use it. Of course, we’ll do this with practical examples that you should be able to adapt to your own unique use cases.
1.3 Why is security important?
The best way to start thinking about why security is important is from your point of view as a user. Like anyone else, you use applications, and these have access to your data. These can change your data, use it, or expose it. Think about all the apps you use, from your email to your online banking service accounts. How would you evaluate the sensitivity of the data that is managed by all these systems? How about the actions that you can perform using these systems? Similarly to data, some actions are more important than others. You don’t care very much about some of those, while others are more significant. Maybe for you, it’s not that important if someone would somehow manage to read some of your emails. But I bet you’d care if someone else could empty your bank accounts.
Once you’ve thought about security from your point of view, try to see a more objective picture. The same data or actions might have another degree of sensitivity to other people. Some might care a lot more than you if their email is accessed and someone could read their messages. Your application should make sure to protect everything to the desired degree of access. Any leak that allows the use of data and functionalities, as well as the application, to affect other systems is considered a vulnerability, and you need to solve it.
Not respecting security comes with a price that I’m sure you aren’t willing to pay. In general, it’s about money. But the cost can differ, and there are multiple ways through which you can lose profitability. It isn’t only about losing money from a bank account or using a service without paying for it. These things indeed imply cost. The image of a brand or a company is also valuable, and losing a good image can be expensive--sometimes even more costly than the expenses directly resulting from the exploitation of a vulnerability in the system! The trust that users have in your application is one of its most valuable assets, and it can make the difference between success or failure.
Here are a few fictitious examples. Think about how you would see these as a user. How can these affect the organization responsible for the software?
A back-office application should manage the internal data of an organization but, somehow, some information leaks out.
Users of a ride-sharing application observe that money is debited from their accounts on behalf of trips that aren’t theirs.
After an update, users of a mobile banking application are presented with transactions that belong to other users.
In the first situation, the organization using the software, as well as its employees, can be affected. In some instances, the company could be liable and could lose a significant amount of money. In this situation, users don’t have the choice to change the application, but the organization can decide to change the provider of their software.
In the second case, users will probably choose to change the service provider. The image of the company developing the application would be dramatically affected. The cost lost in terms of money in this case is much less than the cost in terms of image. Even if payments are returned to the affected users, the application will still lose some customers. This affects profitability and can even lead to bankruptcy. And in the third case, the bank could see dramatic consequences in terms of trust, as well as legal repercussions.
In most of these scenarios, investing in security is safer than what happens if someone exploits a vulnerability in your system. For all of the examples, only a small weakness could cause each outcome. For the first example, it could be a broken authentication or a cross-site request forgery (CSRF). For the second and third examples, it could be a lack of method access control. And for all of these examples, it could be a combination of vulnerabilities.
Of course, from here we can go even further and discuss the security in defense-related systems. If you consider money important, add human lives to the cost! Can you even imagine what could be the result if a health care system was affected? What about systems that control nuclear power? You can reduce any risk by investing early in the security of your application and by allocating enough time for security professionals to develop and test your security mechanisms.
Note The lessons learned from those who failed before you are that the cost of an attack is usually higher than the investment cost of avoiding the vulnerability.
In the rest of this book, you’ll see examples of ways to apply Spring Security to avoid situations like the ones presented. I guess there will never be enough word written about how important security is. When you have to make a compromise on the security of your system, try to estimate your risks correctly.
1.4 Common security vulnerabilities in web applications
Before we discuss how to apply security in your applications, you should first know what you’re protecting the application from. To do something malicious, an attacker identifies and exploits the vulnerabilities of your application. We often describe vulnerability as a weakness that could allow the execution of actions that are unwanted, usually done with malicious intentions.
An excellent start to understanding vulnerabilities is being aware of the Open Web Application Security Project, also known as OWASP (https://www.owasp.org). At OWASP, you’ll find descriptions of the most common vulnerabilities that you should avoid in your applications. Let’s take a few minutes and discuss these theoretically before diving into the next chapters, where you’ll start to apply concepts from Spring Security. Among the common vulnerabilities that you should be aware of, you’ll find these:
Broken authentication
Session fixation
Cross-site scripting (XSS)
Cross-site request forgery (CSRF)
Injections
Sensitive data exposure
Lack of method access control
Using dependencies with known vulnerabilities
These items are related to application-level security, and most of these are also directly related to using Spring Security. We’ll discuss their relationship with Spring Security and how to protect your application from these in detail in this book, but first, an overview.
1.4.1 Vulnerabilities in authentication and authorization
In this book, we’ll discuss authentication and authorization in depth, and you’ll learn several ways in which you can implement them with Spring Security. Authentication represents the process in which an application identifies someone trying to use it. When someone or something uses the app, we want to find their identity so that further access is granted or not. In real-world apps, you’ll also find cases in which access is anonymous, but in most cases, one can use data or do specific actions only when identified. Once we have the identity of the user, we can process the authorization.
Authorization is the process of establishing if an authenticated caller has the privileges to use specific functionality and data. For example, in a mobile banking application, most of the authenticated users can transfer money but only from their account.
We can say that we have a broken authorization if a an individual with bad intentions somehow gains access to functionality or data that doesn’t belong to them. Frameworks like Spring Security help in making this vulnerability less possible, but if not used correctly, there’s still a chance that this might happen. For example, you could use Spring Security to define access to specific endpoints for an authenticated individual with a particular role. If there’s no restriction at the data level, someone might find a way to use data that belongs to another user.
Take a look at figure 1.5. An authenticated user can access the /products/{name} endpoint. From the browser, a web app calls this endpoint to retrieve and display the user’s products from a database. But what happens if the app doesn’t validate to whom the products belong when returning these? Some user could find a way to get the details of another user. This situation is just one of the examples that should be taken into consideration from the beginning of application design so that you can avoid this.
Figure 1.5 A user that is logged in can see their products. If the application server only checks if the user is logged in, then the user can call the same endpoint to retrieve the products of some other user. In this way, John is able to see data that belongs to Bill. The issue that causes this problem is that the application doesn’t authenticate the user for data retrieval as well.
Throughout the book, we’ll refer to vulnerabilities. We’ll discuss vulnerabilities starting with the basic configuration of authentication and authorization in chapter 3. Then, we’ll discuss how vulnerabilities relate to the integration of Spring Security and Spring Data and how to design an application to avoid those, with OAuth 2.
1.4.2 What is session fixation?
Session fixation vulnerability is a more specific, high-severity weakness of a web application. If present, it permits an attacker to impersonate a valid user by reusing a previously generated session ID. This vulnerability can happen if, during the authentication process, the web application does not assign a unique session ID. This can potentially lead to the reuse of existing session IDs. Exploiting this vulnerability consists of obtaining a valid session ID and making the intended victim’s browser use it.
Depending on how you implement your web application, there are various ways an individual can use this vulnerability. For example, if the application provides the session ID in the URL, then the victim could be tricked into clicking on a malicious link. If the application uses a hidden attribute, the attacker can fool the victim into using a foreign form and then post the action to the server. If the application stores the value of the session in a cookie, then the attacker can inject a script and force the victim’s browser to execute it.
1.4.3 What is cross-site scripting (XSS)?
Cross-site scripting, also referred to as XSS, allows the injection of client-side scripts into web services exposed by the server, thereby permitting other users to run these. Before being used or even stored, you should properly sanitize
the request to avoid undesired executions of foreign scripts. The potential impact can relate to account impersonation (combined with session fixation) or to participation in distributed attacks like DDoS.
Let’s take an example. A user posts a message or a comment in a web application. After posting the message, the site displays it so that everybody visiting the page can see it. Hundreds might visit this page daily, depending on how popular the site is. For the sake of our example, we’ll consider it a known site, and a significant number of individuals visit its pages. What if this user posts a script that, when found on a web page, the browser executes (figures 1.6 and 1.7)?
Figure 1.6 A user posts a comment containing a script, on a web forum. The user defines the script such that it makes requests that try to post or get massive amounts of data from another application (App X), which represents the victim of the attack. If the web forum app allows cross-site scripting (XSS), all the users who display the page with the malicious comment receive it as it is.
Figure 1.7 Users access a page that displays a malicious script. Their browsers execute the script and then try to post or get substantial amounts of data from App X.
1.4.4 What is cross-site request forgery (CSRF)?
Cross-site request forgery (CSRF) vulnerabilities are also common in web applications. CSRF attacks assume that a URL that calls an action on a specific server can be extracted and reused from outside the application (figure 1.8). If the server trusts the execution without doing any check on the origin of the request, one could execute it from any other place. Through CSRF, an attacker can make a user execute undesired actions on a server by hiding the actions. Usually, with this vulnerability, the attacker targets actions that change data in the system.
Figure 1.8 Steps of a cross-site request forgery (CSRF). After logging into their account, the user accesses a page that contains forgery code. The malicious code then executes actions on behalf of the unsuspecting user.
One of the ways of mitigating this vulnerability is to use tokens to identify the request or use cross-origin resource sharing (CORS) limitations. In other words, validate the origin of the request. We’ll look closer at how Spring Security deals with CSRF and CORS vulnerabilities in chapter 10.
1.4.5 Understanding injection vulnerabilities in web applications
Injection attacks on systems are widespread. In an injection attack, the attacker employing a vulnerability introduces specific data into the system. The purpose is to harm the system, to change data in an unwanted way, or to retrieve data that’s not meant to be accessed by the attacker.
There are many types of injection attacks. Even the XSS that we mentioned in section 1.4.3 can be considered an injection vulnerability. In the end, injection attacks inject a client-side script with the means of harming the system somehow. Other examples could be SQL injection, XPath injection, OS command injection, LDAP injection, and the list continues.
Injection types of vulnerabilities are important, and the results of exploiting these can be change, deletion, or access to data in the systems being compromised. For example, if your application is somehow vulnerable to LDAP injection, an attacker could benefit from bypassing the authentication and from there control essential parts of the system. The same can happen for XPath or OS command injections.
One of the oldest and perhaps well-known types of injection vulnerability is SQL injection. If your application has an SQL injection vulnerability, an attacker can try to change or run different SQL queries to alter, delete, or extract data from your system. In the most advanced SQL injection attacks, an individual can run OS commands on the system, leading to a full system compromise.
1.4.6 Dealing with the exposure of sensitive data
Even if, in terms of complexity, the disclosure of confidential data seems to be the easiest to understand and the least complex of the vulnerabilities, it remains one of the most common mistakes. Maybe this happens because the majority of tutorials and examples found online, as well as books illustrating different concepts, define the credentials directly in the configuration files for simplicity reasons. In the case of a hypothetical example that eventually focuses on something else, this makes sense.
NOTE Most of the time, developers learn continuously from theoretical examples. Generally, examples are simplified to allow the reader to focus on a specific topic. But a downside of this simplification is that developers get used to wrong approaches. Developers might mistakenly think that everything they read is a good practice.
How is this aspect related to Spring Security? Well, we’ll deal with credentials and private keys in the examples in this book. We might use secrets in configuration files, but we’ll place a note for these cases to remind you that you should store sensitive data in vaults. Naturally, for a developed system, the developers aren’t allowed to see the values for these sensitive keys in all of the environments. Usually, at least for production, only a small group of people should be allowed to access private data.
By setting such values in the configuration files, such as the application.properties or application .yml files in a Spring Boot project, you make those private values accessible to anyone who can see the source code. Moreover, you might also find yourself storing all the history of these value changes in your version management system for source code.
Also related to the exposure of sensitive data is the information in logs written by your application to the console or stored in databases such as Splunk or Elasticsearch. I often see logs that disclose sensitive data forgotten by the developers.
NOTE Never log something that isn’t public information. By public, I mean that anyone can see or access the info. Things like private keys or certificates aren’t public and shouldn’t be logged together with your error, warning, or info messages.
Next time you log something from your application, make sure what you log doesn’t look like one of these messages:
[error] The signature of the request is not correct. The correct key to be used should have been X.
[warning] Login failed for username X and password Y. User with username X has password Z.
[info] A login was performed with success by user X with password Y.
Be careful of what your server returns to the client, especially, but not limited to, cases where the application encounters exceptions. Often due to lack of time or experience, developers forget to implement all such cases. This way (and usually happening after a wrong request), the application returns too many details that expose the implementations.
This application behavior is also a vulnerability through data exposure. If your app encounters a NullPointerException because the request is wrong (part of it is missing, for example), then the exception shouldn’t appear in the body of the response. At the same time, the HTTP status should be 400 rather than 500. HTTP status codes of type 4XX are designed to represent problems on the client side. A wrong request is, in the end, a client issue, so the application should represent it accordingly. HTTP status codes of type 5XX are designed to inform you that there is a problem on the server. Do you see something wrong in the response presented by the next snippet?
{
status
: 500,
error
: Internal Server Error
,
message
: Connection not found for IP Address 10.2.5.8/8080
,
path
: /product/add
}
The message of the exception seems to be disclosing an IP address. An attacker can use this address to understand the network configuration and, eventually, find a way to control the VMs in your infrastructure. Of course, with only this piece of data, one can’t do any harm. But collecting different disclosed pieces of information and putting these together could provide everything that’s needed to adversely affect a system. Having exception stacks in the response is not a good choice either, for example:
at java.base/java.util.concurrent.ThreadPoolExecutor ➥
.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker
➥
.run(ThreadPoolExecutor.java:628) ~[na:na]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable
➥
.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.26.jar:9.0.26]
at java.base/java.lang.Thread.run(Thread.java:830) ~[na:na]
This approach also discloses the application’s internal structure. From the stack of an exception, you can see the naming notations as well as objects used for specific actions and the relationships among these. But even worse than that, logs sometimes can disclose versions of dependencies that your application uses. (Did you spot that Tomcat core version in the preceding exception stack?)
We should avoid using vulnerable dependencies. However, if we find ourselves using a vulnerable dependency by mistake, at least we don’t want to point this mistake out. Even if the dependency isn’t known as a vulnerable one, this can be because nobody has found the vulnerability yet. Exposures as in the previous snippet can motivate an attacker to find vulnerabilities in that specific version because they now know that’s what your system uses. It’s inviting them to harm your system. And an attacker often uses even the smallest detail against a system, for example:
Response A:
{
status
: 401,
error
: Unauthorized
,
message
: Username is not correct
,
path
: /login
}
Response B:
{
status
: 401,
error
: Unauthorized
,
message
: Password is not correct
,
path
: /login
}
In this example, the responses A and B are different results of calling the same authentication endpoint. They don’t seem to expose any information related to the class design or system infrastructure, but these hide another problem. If the messages disclose context information, then these can as well hide vulnerabilities. The different messages based on different inputs provided to the endpoint can be used to understand the context of execution. In this case, these could be used to know when a username is correct but the password is wrong. And this can make the system more liable to a brute force attack. The response provided back to the client shouldn’t help in identifying a possible guess of a specific input. In this case, it should have provided in both situations the same message:
{
status
: 401,
error
: Unauthorized
,
message
: Username or password is not correct
,
path
: /login
}
This precaution looks small, but if not taken, in some contexts, exposing sensitive data can become an excellent tool to be used against your system.
1.4.7 What is the lack of method access control?
Even at the application level, you don’t apply authorization to only one of the tiers. Sometimes, it’s a must to ensure that a particular use case can’t be called at all (for example, if the privileges of the currently authenticated user don’t allow it).
Say you have a web application with a straightforward design. The app has a controller exposing endpoints. The controller directly calls a service that implements some logic and that uses persisted data managed through a repository (figure 1.9). Imagine a situation where the authorization is done only at the endpoint level (assuming that you can access the method through a REST endpoint). A developer might be tempted to apply authorization rules only in the controller layer as presented in figure 1.9.
Figure 1.9 A developer applies the authorization rules at the controller layer.But the repository does not know the user and does not restrict the retrieval of data. If a service asks for accounts that don’t belong to the currently authenticated user, the repository returns these.
While the case presented in figure 1.9 works correctly, applying the authorization rules only at the controller layer can leave room for error. In this case, some future implementation could expose that use case without testing or without testing all the authorization requirements. In figure 1.10, you can see what can happen if a developer adds another functionality that depends on the same repository.
These situations might appear, and you may need to treat these at any layer in your application, not just in the repository. We’ll discuss more things related to this subject in chapters 16 and 17. There, you’ll also learn how you can apply restrictions to each application tier when this is needed, as well as the cases when you should avoid doing this.
Figure 1.10 The newly added TransactionController makes use of the AccountRepository in its dependency chain. The developer must reapply the authorization rules in this controller as well. But it would be much better if the repository itself made sure that data that doesn’t belong to the authenticated user is not exposed.
1.4.8 Using dependencies with known vulnerabilities
Although not necessarily directly related to Spring Security, but still an essential aspect of the application-level security, the dependencies we use need attention. Sometimes it’s not the application you develop that has vulnerabilities, but the dependencies like libraries or frameworks that you use to build the functionality. Always be attentive to the dependencies you use and eliminate any version that’s known to contain a vulnerability.
Fortunately, we have multiple possibilities for static analyses, quickly done by adding a plugin to your Maven or Gradle configuration. The majority of applications today are developed based on open source technologies. Even Spring Security is an open source framework. This development methodology is great, and it allows for fast evolution, but this can also make us more error prone.
When developing any piece of software, we have to take all the needed measures to avoid the use of any dependency that has known vulnerabilities. If we discover that we’ve used such a dependency, then we not only have to correct this fast, we also have to investigate if the vulnerability was already exploited in our applications and then take the needed measures.
1.5 Security applied in various architectures
In this section, we discuss applying security practices depending on the design of your system. It’s important to understand that different software architectures imply different possible leaks and vulnerabilities. In this first chapter, I want to make you aware of the philosophy to which I’ll refer to throughout the book.
Architecture strongly influences choices in configuring Spring Security for your applications; so do functional and nonfunctional requirements. When you think of a tangible situation, to protect something, depending on what you want to protect, you use a metal door, bulletproof glass, or a barrier. You couldn’t just use a metal door in all the situations. If what you protect is an expensive painting in a museum, you still want people to be able to see it. You don’t, however, want them to be able to touch it, damage it, or even take it with them. In this case, functional requirements affect the solution we take for secure systems.
It could be that you need to make a good compromise with other quality attributes like, for example,