Front-End Web & Mobile

How Amazon Cognito Keeps Mobile App Users’ Data Safe

Researchers recently revealed that mobile app developers use weak security and authentication methods in their apps. This puts user data at risk.

Embedding credentials in a mobile application is unsafe, but what’s the alternative and best practice? We offer Amazon Cognito to mobile app developers as the recommended alternative. Amazon Cognito will provide temporary, limited-privilege credentials to each user whether the user is an unauthenticated guest or an authenticated user. By “credentials,” we mean Amazon Cognito provides a unique AWS access key and secret key to each user. These credentials and their distribution mechanism have a few important properties that increase security over embedding shared credentials:

  • They are produced for the current user, so nothing is embedded in your app’s binary files or configuration for a malicious user to capture and reuse.
  • They expire after a short period of time so that, in the unlikely event a malicious user was able to nab the credentials, they won’t be usable for long.
  • They are also limited in privilege. When you set up your identity pool in Amazon Cognito, you can give unauthenticated guests permissions that are different from those given to authenticated users.

In this article, we’ll explain how to switch to Amazon Cognito and touch on some other ways you can use Amazon Cognito to protect your users.

Amazon Cognito as an Alternative to Embedded Credentials

Here is an example of what not to do (initializing the AWS Mobile SDK with embedded credentials):

// Warning! Example of what not to do! Don't do this!
AWSStaticCredentialsProvider *credentialsProvider = [AWSStaticCredentialsProvider credentialsWithAccessKey:"your-access-key" secretKey:"your-secret-key"];
AWSServiceConfiguration *configuration = [AWSServiceConfiguration configurationWithRegion:AWSRegionUSEast1 credentialsProvider:credentialsProvider];
[AWSServiceManager defaultServiceManager].defaultServiceConfiguration = configuration;

Here is the same code using the more secure Amazon Cognito credential provider:

AWSCognitoCredentialsProvider *credentialsProvider = [[AWSCognitoCredentialsProvider alloc] initWithRegionType:AWSRegionUSEast1 identityPoolId:@"your-cognito-identity-pool-id"];
AWSServiceConfiguration *configuration = [[AWSServiceConfiguration alloc] initWithRegion:AWSRegionUSWest2 credentialsProvider:credentialsProvider];
[AWSServiceManager defaultServiceManager].defaultServiceConfiguration = configuration;   

As you can see, the difference here is that we embed the Amazon Cognito identity pool ID instead of your access key and secret key. At first, you may wonder why this is any more secure. The biggest difference is that rather than using a single set of credentials for all users of your app, each user is issued unique credentials for calling your AWS resources. Amazon Cognito provides this unique user identifier for the current user of your app and facilitates user login with social login providers like Amazon, Facebook, Twitter, Google, and any OpenID Connect-compatible provider, or your own user authentication system.

Anyone who reverse engineers your application and retrieves the identity pool doesn’t gain additional access. The identity pool is not secret. Anyone who uses it will get a different Amazon Cognito ID. You can easily prevent him or her from seeing data from your other users using the techniques described below.

Protecting User-Specific Data with the Cognito ID

Every user of your app, even guests, will get a unique identifier called a Cognito ID, which looks something like this:

us-east-1:1234abcd-1234-abcd-ab12-abcd1234abcd

For authenticated users, the same ID will be issued whenever they log in to your app, even if it is from a different device or even if they use multiple social providers. You can use the Cognito ID to protect assets and user data. For example, you can store the ID as the owner in a column in a DynamoDB table, and use IAM policies to prevent anyone else from reading or changing the user’s data. For more information, see IAM Roles in the Amazon Cognito documentation. Similarly, you can use the ID as a bucket prefix on Amazon S3 where the user’s photos are stored so that only the user can download or upload photos to the bucket. Just using this identifier gives your users a great deal of extra security because even if a malicious user gets enough information to make calls into your AWS resources, each user’s data is behind a unique identifier, making it next to impossible for someone to access another user’s data.

NOTE: Use caution when using the Cognito ID in this way for unauthenticated users. Unauthenticated user IDs may change when not used for a long period of time and will not be the same from multiple devices. Also, because unauthenticated users are generally ephemeral users, it doesn’t usually make sense to permanently store data unique to them.

Authenticated Users

Authenticated users are more trustworthy so you can grant them more access to resources. To authenticate a user with Amazon Cognito, you pass a token from the login provider. When users sign in with your app using a social provider like Facebook, your app’s code receives a token from the social provider’s SDK. That token is passed along to Amazon Cognito to help establish an identity, like so:

NSString *token = [FBSDKAccessToken currentAccessToken].tokenString; // currentAccessToken comes from the Facebook SDK
credentialsProvider.logins = @{ @(AWSCognitoLoginProviderKeyFacebook): token };
NOTE: The credentials providers included in the AWS Mobile SDKs defer the call to the service until absolutely necessary. This means that until you actively use the credentials provider you may not see an authenticated user in the Amazon Cognito console.

By authenticating the user you get additional layers of protection:

  • First, the user logged in at the provider with a valid account. This increases the likelihood the user is a real person. You can be reasonably sure that one person doesn’t have dozens or hundreds of accounts because most social login providers take steps to prevent such abuse.
  • Second, the Facebook SDK and other social login provider SDKs take steps to increase the likelihood the login comes from your app. The implementation details vary, but generally they use the bundle identifier on iOS, which is provided as part of the provisioning process in the App Store. On Android, they generally leverage the fact the APKs for distribution are signed with the developer key. All bets are off on a jailbroken or rooted device, but these measures increase the likelihood the authentication request comes from your app. Nonetheless, we recommend you write your application as if a user can make any AWS call using his or her Cognito-provided credentials. In this way, your application will be resilient against any jailbroken, rooted, or otherwise modified devices and callers.

After Amazon Cognito receives the token, the service makes a call back to the provider to validate the identity of the token. The authentication flow looks like this:

Note the validation step. By validating the token with the provider, Amazon Cognito will detect scenarios in which the token has expired or been revoked by the provider. This can happen, for example, if the provider detects a fake or malicious account and disables it. If Amazon Cognito can’t validate the token, it will not authenticate the user and issue authenticated credentials. The user won’t be able to use your AWS resources with a bogus account.

Secure Your Mobile App with Amazon Cognito Today!

We’ve explained how Amazon Cognito can be used instead of embedding credentials into your apps, a practice security researchers have called “the weakest form of authentication.” Rather than embedding credentials in your apps, use authenticated users with Amazon Cognito to increase the security of your app. Use the Amazon Cognito ID to lock down data to each authenticated user so that it cannot be discovered or read by a malicious user.