AWS Cognito vs. Spring Security - Our Weigh In.png

AWS Cognito vs. Spring Security: Our Weigh In

A good learning culture has many benefits for your organization, your employees and clients. It may take time, but here at Intertec we constantly work on nurturing a knowledge-sharing environment, where employees can share what they’ve learned with others and improve the organization’s expertise.

That being said, this month in a collaboration article together with my colleague Vlatko we discussed a few key points regarding Spring Security vs. Cognito. Read on to learn about the differences between Cognito and Spring Security and discover how we came to the conclusion that when it comes to implementing authentication and authorization in a custom software solution project Cognito is the way to go.

Does it save development time?

Martin on Cognito: When it comes to answering the most important question of clients and project managers “How quickly can we implement global authentication and authorization in our system?”, Cognito comes to the rescue. Cognito is an out-of-the-box solution, which can be set up manually or via CloudFormation (later referred to as CF) templates.

However, when running multiple projects on AWS infrastructure, it is more viable to use CF templates. CF offers two ways of writing the template, JSON or YAML. It usually takes approximately 2 minutes to set it up from scratch (depending on which region is deployed).

Furthermore, besides the setup, it provides a customizable UI for login, registration, password recovery, password change, federated authentication and Multi-factor authentication, later referenced as MFA, (using SMS, e-mail and One-time password), meaning we don’t put additional headache on the engineers.

Vlatko on Spring Security: Adding Spring Security to a Spring Boot application is as easy as adding Spring Security to the classpath. For Spring applications that don’t use Spring Boot an additional configuration is needed. The following things are achieved with this simple setup:

  • All of the application endpoints are already secured
  • The login form is generated
  • Form-based login and HTTP Basic security are enabled
  • One default user is created which can be used to login and access the endpoints (whose password is printed to the console on runtime)
  • CSRF attack prevention is enabled
  • Session Fixation protection is enabled

Adding one additional annotation or extending one additional class in some of our configuration classes, we already have the option to use method-level security and it is as simple as using additional annotation on the method which we want to secure additionally. This basic setup can easily be extended or modified depending on the needs of the application by overriding methods of the already defined beans or redefining them.

For example, we can easily disable the CSRF protection, extend the global CORS configuration, set which endpoints should be secured, extend the default configuration for authentication and authorization, etc. As we can see it is pretty easy and fast to secure an application by using the initial default basic setup provided by Spring Security.

Often, this basic setup is not enough and in those cases, the development time depends on the requirements for the application security. However, there are a lot of out-of-the-box options provided by Spring Security that can be easily modified or extended. This accelerates the process of development and saves development time.

Is it both reliable and secure?

Martin on Cognito: Usually, our clients are in a need of strong authentication and authorization service. By the term “strong” they mean reliable, happy customers not having any problems in terms of logging in, registering, password changes and security of personal data. Having in mind what stands behind Cognito, the almighty Amazon Web Services, makes it easier to answer this type of question. As a fully managed service, it guarantees at least 99.9% Monthly Uptime Percentage, so our worries regarding availability are put aside.

When it comes to security it has basic and advanced security features. On one side, it provides encryption of data on the application level via the AWS Amplify module. On the other hand, advanced security features protect user accounts from unauthorized access.

Cognito has a smart risk calculation feature that detects users who have entered credentials that have been compromised elsewhere and prompts them to change their password. Data, as such, is stored in its secured and heavily guarded database which is only accessible via AWS Amplify or its own API. Security of content stored in Cognito is provided by AWS. It applies a shared responsibility model for data protection meaning both AWS and its customers are responsible for protecting the data.

In Cognito, AWS has a big impact, using its own encryption keys and not supporting customer-defined keys. However, the following principles make the content fully industry compliant and secure:

  • MFA with each customer account
  • Using SSL/TLS to communicate with Cognito
  • In Transit encryption, meaning usage of cipher suites with perfect forward secrecy (PFS) such as Ephemeral Diffie-Hellman (DHE) or Elliptic Curve Ephemeral Diffie-Hellman (ECDHE)

It is compliant with the following standards/programs: SOC, PCI, ISMAP, FedRAMP, DoD CC SRG, HIPAA BAA, IRAP, MTCS, C5, K-ISMS, ENS High, OSPAR, HITRUST CSF, FINMA.

Vlatko on Spring Security: The Spring Security reliability depends on the third-party services that are used (such as database access or Lightweight Directory Access Protocol server) depending on the configuration setup. Regarding security, Spring Security is secure as long as it is configured and used properly.

Spring Security in the web tier is based on beans of type Filter through which the requests are processed and each one of them can be used to prevent downstream Filters from being invoked. Besides securing web applications, Spring Security offers support for using method-level security. Web security and method-level security can be combined in the same application. The Filter chain provides user experience features, such as authentication and redirects to login pages and the method security provides protection at a more granular level.

Spring Security offers protection against some of the most popular exploits out of the box like CSRF attack, Session Fixation, adds Security Headers to the responses, and offers configurable CORS support. There is no easy, out-of-the-box solution to enable application-level data encryption. Spring Security supports the encryption of data by using the Security Crypto Module which provides support for symmetric encryption, key generation, and password encoding. Besides the usual password encoding, we can use this module to encrypt/decrypt data in/from the database but to achieve this we need to add our custom attribute to a database column (and vice versa) converter.

Handling authorization, is it supported and easy to implement?

Martin on Cognito: When it comes to authorized users in the system, I would say it depends on the requirements and the level of granularity the role permission model needs to be set. Cognito supports this via the Identity pool in combination with IAM roles. These two combined, give us the ability to control access from as large as AWS services to as granular as a row of a table in DynamoDB. Setting up authorization in Cognito is pretty straightforward following the next steps:

  • Define a user pool that will serve as a user directory
  • Create an Identity pool and integrate it with the user pool
  • Create an IAM role/s and define what it will authorize
  • Create a group in the user pool and assign the IAM role to it

With these four steps taken into account, you are ready to use Cognito, not just as your authentication component, but also as an authorization endpoint as well. Handling multiple roles and different permissions for them is viable through the management of user groups in the User Pool.

Sometimes a question is raised about whether a user can have multiple roles assigned. The answer is yes it can, with only adding that user to the desired group. On one side, it takes little effort to set an ACL model, but on the other, to this extent, it can be a bit of a challenge.

Vlatko on Spring Security: Regardless of how you want to authenticate, the authorization services can be used in your application consistently and simply.

The “AccessDecisionManager” is the main interface responsible for making access-control decisions in Spring Security by using the “decide” method which takes an “Authentication” object. All “Authentication” interface implementations have a list of “GrantedAuthority” objects which represent the authorities that have been granted to the principal. The “AuthenticationManager” is responsible for adding the “GrantedAuthority” objects into the “Authentication” object which are later read by “AccessDecisionManager” when making authorization decisions. Spring Security includes one specific “GrantedAuthority” implementation out of the box – “SimpleGrantedAuthority”.

Spring Security in the web tier is based on a security filter chain that has a request matcher that is used to decide whether to apply it to an HTTP request. However, within the filter chain, you can have more fine-grained control of authorization by setting additional matchers in the HttpSecurity configurer. As well as support for securing web applications, Spring Security offers support for applying access rules to Java method executions. Spring Security uses Spring EL as an authorization mechanism that allows complicated boolean logic to be encapsulated in a single expression.

Four annotations support expression attributes to allow pre/post invocation authorization checks: @PreAuthorize, @PreFilter, @PostAuthorize and @PostFilter. Taking this into consideration, to customize the authorization in Spring Security we should do the following:

  • Implement our own “UserDetailsService” which is used by the “AccessDecisionManager” when making authorization decisions. In the “UserDetailsService” we should set the “GrantedAuthority” objects for the user. Here we should also set other properties for the user account (if the account is enabled, expired or locked or if the credentials are expired).
  • Set how and when authorization will be required by customizing the security filter chain by setting the HttpSecurity configurer.
  • Use @EnableGlobalMethodSecurity annotation (on any @Configuration instance) if we want to use annotation-based security for applying access rules to Java method executions.
  • Make sure that appropriate authorities will be assigned to a user when a user is created/registered.

The most common implementation is that for each user we will have multiple roles and for each of the roles we will have multiple authorities/privileges.

Are there any specific tradeoffs that need to be done?

Martin on Cognito: The answer is YES and there is mainly one tradeoff. When it comes to authorization, as I mentioned in the previous section, Cognito provides us with a mechanism via IAM roles to specify access to certain services in the cloud. How can that be implemented by configuring an identity pool that will use Cognito user pool as an authentication method and one or more IAM role/s that will represent a single role with specific permissions?

When it comes to questioning the complexity of the role/permission model that can be developed with Cognito, the answer is: Cognito supports that to a certain extent, but puts more effort into reliability, consistency, and availability. By “certain extent” I mean, for example, if you want to create a hierarchy of roles within a system then you should create group/s of users in the user pool. That group can have a single IAM role assigned to it. And, if you want to give permissions, to a specific user, for a service not available for that group what you will need to do is to put that user in another group that will have that permission for the specific service. This can quite complicate things when it comes to giving permissions and the flexibility in doing so leading to cumbersome groups, users and IAM roles.

Vlatko on Spring security: We can say that Spring in general (not just Spring Security) has a steep learning curve. That is because the Spring Framework is actually a collection of multiple projects designed to address different aspects of enterprise application development. However, once you’ve learned Spring Framework, you’ll become more productive in addressing business functionality by allowing the framework to handle common tasks.

Another worth mentioning tradeoff is that there is no easy out-of-the-box solution to enable application-level data encryption. To achieve that, we need to learn about the Security Crypto Module. We can use this module to encrypt/decrypt data in/from the database but to achieve this we need to add our custom attribute to a database column (and vice versa) converter. Also, as a workaround, filesystem encryption or database encryption (if supported by the database engine) can be used instead.

It’s not just about the service. What about the costs?

Martin on Cognito: When using Cognito, charging is based on monthly active users (MAUs). This means that within a calendar month, there is an identity operation related to a user, such as sign-up, sign-in, token refresh or password change. But charging money is not what AWS services are all about. There is quite a large so-called Free Tier in Cognito that supports 50,000 MAUs. The good thing about it is that it doesn’t expire after the 12-month free period. Also, charges are not applied for subsequent sessions and inactive users within the month.

Even on our big projects where we have more than 50,000 MAUs, the price to pay is very small. The table below shows the pricing tiers.

AWS Cognito vs. Spring Security - Our Weigh In 2.png

Advanced security features also have pricing. Given below is a table of pricing per tier:

AWS Cognito vs. Spring Security - Our Weigh In 3.png

For example, if we have a system where the number of monthly active users is 60,000 MAU the calculations are the following:

  • 0 SAML or OIDC federation MAUs – 50 free SAML or OIDC federation MAU requests per month = -50.00 billable SAML or OIDC federation MAU requests
  • Max (-50.00 billable SAML or OIDC federation MAU requests, 0 minimum billable SAML or OIDC federation MAU requests) = 0 total billable SAML or OIDC federation MAU requests
  • SAML or OIDC federation cost (monthly): 0 USD
  • 60,000 MAUs – 50000 free MAU requests per month = 10,000.00 billable MAU requests
  • Tiered price for: 10000.000000 MAUs
  • 10000 MAUs x 0.0055000000 USD = 55.00 USD
  • Total tier cost = 55.00 USD (User Pool MAUs)
  • User Pool MAU cost (monthly): 55 USD
  • Advanced security feature cost (monthly): 0 USD
  • Cognito MAU cost (monthly): 55.00 USD

Vlatko on Spring Security: Spring Security is itself free of charge. However, if you choose to have your application secured by using Spring Security, the additional costs will depend on the third-party services that they are already using.

And to conclude, why Cognito?

AWS Cognito is the better choice when it comes to implementing advanced security features like phone number validation, multi-factor authentication, or federated authentication. It offers a complete user identity management system that allows you to build great user experiences for the end-users across multiple platforms. It is a faster, more reliable, secure and scalable solution where you do not have to worry about the backend because this is already provided by the service itself.

And last but not least, it is a great tool to implement authentication and authorization for your projects or services.

Are you someone who is in a search of a new challenge? Do you want to be in a company where out-of-the-box thinking is encouraged? Check out our current job openings.

Martin Ristevski

May 19, 2021

Category

Article

Technologies

JavaAWSAmazon CognitoSpring SecurityDevOps

Let's talk.

Name
Email
Phone
By submitting this, you agree to our Terms and Conditions & Privacy Policy