This tutorial is adapted from Web Age course Architecting Microservices with Kubernetes, Docker, and Continuous Integration Training.
1.1 Why Microservice Security?
Security is important in all systems and more complicated in a distributed system. We can no longer put our application behind a firewall and assume that nothing will breakthrough. Testing the security of microservice is required and may vary slightly depending on how you implement security. Individual services may just be doing token validation, which means we have to test at the individual service level. Using another service or a library to validate tokens, requires our services to be tested in isolation with those interactions being tested and validated in a production-like environment.
1.2 Security Testing in Microservices
Testing the security of microservice is required and may vary slightly depending on how you implement security. Individual services may just be doing token validation, which means we have to test at the individual service level. Using another service or a library to validate tokens, requires our services to be tested in isolation with those interactions being tested and validated in a production-like environment.
1.3 Security Topology
Collections of microservices are grouped as applications, and surface external APIs as endpoints through a gateway. One obvious difference above, from monolithic to microservice, is the number of moving parts. The other is how many more requests there are. In microservice enterprise architecture, you need to be aware of the possibility that the applications will make or respond to many millions of requests and responses.
1.4 Authorization and Authentication
- Establishing the user’s identity is performed by a dedicated, centralized service or an API gateway.
- Central service could then further delegate user authentication to a third party.
- When establishing a user’s authority or permission to access a secured resource in a microservices environment keep group or role definitions coarse-grained in common, cross-cutting services.
- Allow individual services to maintain their own fine-grained controls.
- This is different than typical monolithic applications where fine-grained roles are stored in the application database or some centralized mechanism. For microservices, this could be an antipattern due to the need to modify that central resource when deploying or modifying the services.
1.5 J2EE Security Refresh
Each Java EE server service must be trusted by all of the other services. Users receive security tokens after authentication (in WebSphere Application Server, this is an LTPA Token). User tokens must be valid on all servers. Trust stores are defined so that every server has the certificates of every other server. All services must use the same finite set of key(s). All J2EE servers for the same application(s) must be in the same security realm
1.6 Role-based Access Control in a Nutshell
Implementation of Role-based authentication in code, has our system with an action for creating and updating customers, that we want the ‘Sales’ role to have access to, and so we write code like this:
Later, we realized that, sometimes, people from the ‘Operations’ role should be able to create and update Customers. Now we update the Action method as:
Later again, we realize that some of the operations people should not be able to do this action, but it is not possible to assign a different role for those people who are in Operations. So, we are forced to allow all operations people to create and update Customers.
Anytime we change the roles, people are assigned, that should be allowed to create and update customers, we have to update all of our MVC Action methods Authorize attributes, build & deploy, test, and release our application.
Finally, we realize that ‘Operations’ is the wrong additional group for this role and we needed ‘Marketing’, now we’re updating code again across a bunch of services, and re-releasing.
1.7 Claim-based Access Control in a Nutshell
We define some set of claims like this :
“AllowCreateCustomer”, “AllowUpdateCustomer”, “AllowEditCustomer”
Now, we can decorate our Action Method like this:
We can see that, CreateCustomer action method will always need permission ‘CanCreateCustomer’ and it will very likely never change. In our data store, we create a set of permissions (claims). In our data store, we create the user related to the permissions. From our administration interface, we can set permission (claim) for each user who can do an action or operation. We can assign ‘CanCreateCustomer’ permission (claim) to anyone who should be permitted. Users will only be able to do only what has been assigned from a permissions perspective. Claim information is usually passed between requests as a cookie to avoid database access, but again with a view to security and performance
1.8 Sharing Sessions
When we split authentication off from a “monolith” application, we have two challenges to contend with:
- Sharing cookies between the auth server(s) and application server(s) On one server on one domain, this was not an issue. With multiple servers on multiple domains, it is. We’ll address this challenge by running all servers under one domain and proxying to the various servers.
- Sharing a session store across server(s) With a single monolith, we can write sessions to disk, store them in memory, or write them to a database running on the same container. This won’t work if we want to be able to scale our application server to many instances as they will not share a memory or a local filesystem. We address this challenge by externalizing our session store and sharing it across instances.
1.9 Session Cookie
JSON Web Tokens need to be transient. Creating long-lived JWTs isn’t practical, as they’re self-contained and there’s no way to revoke them.
Upon successful authentication a PersistentJwtTokenBasedRememberMeServices
- creates a persistent Session object
- saves it to the database
- converts the Session into a JWT token
- persist Session to a cookie on the client’s side (Set-Cookie)
- create transient JWT
JWT is meant to be used through the lifetime of the single page front-end and passed in the HTTP header (X-Set-Authorization-Bearer).
1.10 JSON Web Token (JWT)
JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWT contains the header and the payload. The first part is the token’s header which identifies the token’s type and the algorithm used to sign the token. The second part is the JWT token’s payload or its claims. There’s a distinction between these two. A payload can be an arbitrary set of data, it can be even plain text or another (nested JWT). Claims on the other hand are a standard set of fields.
1.11 Spring Security
Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications.
Spring Security features
- Comprehensive and extensible support for both Authentication and Authorization
- Protection against attacks like session fixation, clickjacking, cross-site request forgery
- Servlet API integration
- Integration with Spring Web MVC
- Microservice security requires new techniques from monolithic applications
- JSON Web Tokens can be created and manipulated by multiple frameworks
- Role-based and claim-based security allows teams to secure applications