Guides

Upload an Encrypted File to S3 Using Next.js

This guide will help you save an encrypted file to a S3 bucket. Clone this GitHub repo or fork this Replit to follow along.

Amazon S3 (Simple Storage Service) is a cloud-based storage service offered by Amazon Web Services (AWS) that provides highly scalable, secure, and durable object storage. If you’ve made it to this guide, there’s a good chance you’re using it now or have before.

Amazon S3 provides encryption at rest using Advanced Encryption Standard (AES) 256-bit encryption. However, experts suggest that this may not be enough security assurance.

In this guide you will create a simple Next.js app that encrypts an uploaded file using the Evervault React SDK and and saves the encrypted file to a S3 bucket via a presigned url.

Prerequisites

Create a new S3 bucket

First go to your AWS console into Amazon S3 > Buckets.

  1. Click “Create bucket”
  2. Give your bucket a name
  3. Choose a region for your bucket
  4. Uncheck “Block all public access”

Configure the S3 bucket

For the sake of this demo, to avoid access and CORS issues we will allow public access and any origin to make a request. If you plan to run this in production you should change the public access settings, and update the below configuration to only allow specific hosts.

In your new bucket, go to the permissions tab. In the bucket policy section, click edit and paste in the below.

1
[
2
{
3
"AllowedHeaders": [
4
"*"
5
],
6
"AllowedMethods": [
7
"PUT",
8
"POST",
9
"DELETE"
10
],
11
"AllowedOrigins": [
12
"*"
13
],
14
"ExposeHeaders": []
15
}
16
]

This will allow you to make the PUT request to your bucket to upload the file.

Create an Evervault app

After you have created or logged in to your Evervault account, create a new app. In the app, go to your app settings dashboard. You will need your App ID and Team ID in a few steps — they should look like app_XXXX and team_xxx.

Set up the app

Environment Variables

Open your favorite text or code editor and go to .env.example. Rename it .env and add the following variables from your Amazon and Evervault accounts:

1
# EVERVAULT
2
EVERVAULT_API_KEY="ev_XXXXXXXX"
3
4
#AWS
5
S3_REGION='your-region'
6
ACCESS_KEY=XXXXX
7
SECRET_ACCESS_KEY=XXXXX
8
BUCKET_NAME="your-bucket-name"

If you decide to deploy this to Vercel make sure you don’t use AWS in your variable names as these could be reserved variables.

Client

Open up _app.js to see where the SDK is instantiated.

1
<EvervaultProvider teamId={process.env.NEXT_APP_TEAM_ID} appId={process.env.NEXT_APP_APP_ID}>
2
<ChildComponent />
3
</EvervaultProvider>

Now in ChildComponent see the following code.

1
import { useState } from 'react';
2
import { useEvervault } from '@evervault/react';
3
4
export default function ChildComponent() {
5
const evervault = useEvervault();
6
7
const [selectedFile, setSelectedFile] = useState("");
8
const [encrypted, setEncrypted] = useState("");
9
10
async function uploadFile(e) {
11
e.preventDefault();
12
const fileName = encrypted.name
13
const fileType = encrypted.type
14
const res = await fetch(`/api/get-url?fileName=${fileName}&fileType=${fileType}`);
15
const { url } = await res.json()
16
17
const upload = await fetch(url, {
18
method: 'PUT',
19
body: encrypted,
20
headers: { "Content-Type": fileType }
21
})
22
if (upload.ok) {
23
console.log('Uploaded :)')
24
} else {
25
console.error('Upload failed :(')
26
}
27
};
28
29
async function encryptFile(input) {
30
let encrypted = await evervault.encrypt(input);
31
console.log(encrypted);
32
setEncrypted(encrypted);
33
}
34
35
return (
36
<div>
37
<input
38
type="file"
39
id="myFile"
40
name="myFile"
41
value={selectedFile}
42
onChange={(e) => encryptFile(e.target.files[0])}
43
/>
44
<button onClick={(e) => uploadFile(e)}>
45
save to s3
46
</button>
47
</div>
48
);
49
}

In this code you are rendering a file upload input, which will encrypt a file as soon as it’s uploaded. The encrypted file is logged in case you want to check to see that it is indeed encrypted. You then make a request to the backend to generate a presigned URL using the AWS SDK. Once you have the presigned URL, you will make a PUT request to handle the object upload.

Server

In src/pages/api/get-url.js, you should see the below:

1
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
2
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
3
import * as dotenv from 'dotenv'
4
5
dotenv.config()
6
7
export default async function handler(req, res) {
8
9
const clientParams = {
10
credentials: {
11
accessKeyId: process.env.ACCESS_KEY,
12
secretAccessKey: process.env.SECRET_ACCESS_KEY
13
},
14
region: process.env.S3_REGION
15
};
16
17
const client = new S3Client(clientParams);
18
const command = new PutObjectCommand({
19
Bucket: process.env.BUCKET_NAME,
20
Key: req.query.fileName,
21
ContentType: req.query.fileType,
22
})
23
24
const preSignedUrl = await getSignedUrl(client, command, { expiresIn: 3600 });
25
26
res.status(200).json({
27
"url": preSignedUrl
28
})
29
}

This is where the presigned URL is generated. You will need to pass in your credentials stored as environment variables, then use the PutObjectCommand() method from the AWS SDK to generate the correct type of URL. The URL is set to expire in one hour but you can adjust this amount as needed. Once the URL is generated, you return it to the client.

Try it out

Install the dependencies using your preferred package manager.

1
npm install
2
pnpm install
3
yarn install

Then run the code.

1
npm run dev
2
pnpm run dev
3
yarn run dev

Add a file to upload and then click the save to s3 button. If working properly you should see your encrypted files uploaded in the bucket.

Files successfully saved to intended S3 bucket

Conclusion

In this guide, you uploaded an encrypted file to s3 using the Evervault React SDK with Next.js.

If you ran into any issues or have questions about this guide, feel free to raise them on GitHub or drop them in the Evervault Discord. Keep sensitive files safe!