Serverless Security - Cognito Misconfigurations

makit

Martyn Kilbryde

Posted on July 28, 2024

Serverless Security - Cognito Misconfigurations

Serverless Security - Cognito Misconfigurations

Serverless services offer huge benefits when it comes to security due to the fact that you don't need to manage the infrastructure and they are ready-to-use with a strong base-level of security.

Due to this, then there are people using Serverless services such as Cognito who don't fully understand the service and the reponsibilities of security that fall on the developer!

Below I look into two possible misconfigurations for the Amazon Cognito service. This is a service from AWS that let's you add sign-up and authentication capabilities to your application quickly and easily.

Cognito Email Address Updates

This first issue is based on a critical security issue that was found on Flickr in 2021. The bug report can be read on HackerOne.

I have put together a simple example on GitHub which allows you to deploy and try this out yourself. In this sample application there is a basic API Gateway REST API configured with auth against a Cognito user pool. This has an endpoint for reading the currently logged in users profile data (this is private information).

The web application allows creating and logging in of accounts against this Cognito user pool, and once logged in it will hit the REST API to show the private user profile of the logged in user.

The database lookup is performed with the unique email address, which is also the username for logging in.

Taking Advantage

Once logged in as bob@fakeemail.com then bob can take the Cognito access token from his browser and use it with the AWS CLI to hit Cognito directly to update their own email address to another users:

aws cognito-idp update-user-attributes --access-token ACCESS-TOKEN --user-attributes Name="email",Value="gary@fakeemail.com"

But this will fail due to the fact the email already exists: An error occurred (AliasExistsException) when calling the UpdateUserAttributes operation: An account with the given email already exists.

But do it again with a different capitalisation, such as GARY@fakeemail.com and Cognito may allow it (dependent on configuration). Case sensitivity needs to be considered for any of the Cognito attributes.

If the user logs out and back in now then the database lookup will occur for gary@fakeemail.com (rather than bob) and pull back his personal data.

Protection

  • If using the email address as a lookup then ensure its verified first (email_verified). Ideally block login and only allow them to progress once verified.
  • Use the Keep original attribute value active when an update is pending option. In CDK this is: keepOriginal: { email: true }
  • Use an ID for lookups, not the email address if it can change. This is a general best practice.
  • Turn off case sensitivity with email addresses. In CDK: signInCaseSensitive: false
  • Set permissions on attributes so the user cannot update them unless they really need to.

Cognito UserID Within Attribute Update

This second example is for the developers who are following a best practice of storing a user ID as an attribute, rather than using the sub.

This is a recommendation over using the sub because it allows migrating of users to different user pools, whereas the sub is static and cannot be changed if a migration occurs. This could cause large issues if used as the unique ID within a database and if you want the ability to fail over to a different region.

Again, for this I have put together a simple example in GitHub which allows you to deploy and try this out yourself.

Similar attacks to this are possible if attributes are used to store the users access level, such as isAdmin or similar. It's worth noting that a user can use the CLI to load all the attributes for themselves, so don't store information in them that you wouldn't want a user to see about themselves.

Taking Advantage

Let's say you have a userId attribute set of 99999999-9999-9999-9999-999999999999. If this is mutable then the user could simply update it to be the userId of somebody else:

aws cognito-idp update-user-attributes --access-token ACCESS-TOKEN --user-attributes Name="custom:userId",Value="55555555-5555-5555-5555-555555555555"

Logout and back in and now they are in essence logged in as somebody else.

If the user ID is available from the frontend somehow (a user list page or from a comment from another user) then this is a big issue. If it's not shared publically in anyway then it would mean the bad actor would need to guess a GUID so its less serious, but still bad.

Protection

  • Don't set the attibute as mutable, in this scenario then once set at registration then it cannot be updated by the user.
  • If it needs to be mutable (maybe you are migrating to using an attribute for existing users) then ensure the app client used on the frontend doesn't have permissions to update the attribute.
  • Ensure you use randomised IDs such as GUID/UUIDs, rather than an integer. This alone is not enough to protect an application, but having incrementing IDs is not a good idea.
💖 💪 🙅 🚩
makit
Martyn Kilbryde

Posted on July 28, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related