How can I hide S3 keys in a javascript (react native) application from the user?

Viewed 596

I have an app that uploads files to an S3 bucket as part of it's functionality. I've gotten the app to work but the keys to the bucket are in the app files so a user can just see the keys if sufficiently motivated. I've already configured the key to have limited permissions but what can I do to prevent the user from seeing the keys at all or at least obfuscate them?

2 Answers

There is little you can do to hide them from your user if the frontend of your application needs to read them.

Depending on your use case there are a few scenarios you could take a look at.

Firstly you could replace the permanent credentials with temporary credentials, to do this you can make use of the service AWS Cognito.

By using this you can generate temporary credentials based on one of the following approaches:

  • If the user has to login, they can be attached to a cognito user that will have temporary credentials generated for them.
  • Cognito also supports an anonymous user (a guest user) which also grants temporary credentials.

Another approach you can take if the application is primarily write based is to use a pre signed S3 URL to upload objects. This would combine an API Gateway with a Lambda to generate this for you, therefore no credentials would be stored on the frontend. Take a look at this article for more information on how to handle the approach.

You need presigned URLs to provide upload functionality without exposing the keys. Suppose you have a backend, such as a Lambda function, that has the keys that allow uploading objects.

You need an endpoint on your backend that the app can reach. Then generate a signed URL:

const url = s3.createPresignedPost({
    Bucket: <bucket>,
    Fields: {
        key: <filename>,
    },
});

On the app's side, get this signed URL from the backend (I don't know about React native, this code is written for the browser but it should be easy to modify it to other environments if needed):

const url = await (await fetch("<backend url>")).json();

Create a form with the file:

const formData = new FormData();
formData.append("Content-Type", file.type);
Object.entries(data.fields).forEach(([k, v]) => {
    formData.append(k, v);
});
formData.append("file", file); // must be the last one

Then send the file:

await fetch(data.url, {
    method: "POST",
    body: formData,
});

There are a lot of subtle things you can set up to make sure it's secure. See this article for more info and example code.

Related