How to Work with AWS S3 Pre-Signed URLs
Introduction
Amazon Simple Storage Service (S3) is a scalable, high-speed, web-based cloud storage service designed for data storage and retrieval. Pre-signed URLs are a secure way to grant temporary access to objects in your S3 buckets without exposing your AWS credentials.
In this blog post, we’ll delve into how to generate and use pre-signed URLs for secure uploads and downloads, providing code examples in both PHP and JavaScript.
Understanding AWS S3 Pre-Signed URLs
What Are Pre-Signed URLs?
A pre-signed URL is a URL that grants temporary access to a specific S3 object. The URL is signed with your credentials and can be configured to allow actions like uploading (PUT) or downloading (GET) for a limited time.
Use Cases
- Providing temporary access to private objects.
- Allowing users to upload files directly to S3.
- Generating secure download links.
Generating Pre-Signed URLs for Secure Uploads
Using AWS SDK for PHP
Install the AWS SDK for PHP using Composer:
1
composer require aws/aws-sdk-php
Generate a pre-signed URL for uploading:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php
require 'vendor/autoload.php';
use Aws\S3\S3Client;
use Aws\Exception\AwsException;
// Initialize S3 client
$s3Client = new S3Client([
'version' => 'latest',
'region' => 'us-west-2',
'credentials' => [
'key' => 'YOUR_AWS_ACCESS_KEY_ID',
'secret' => 'YOUR_AWS_SECRET_ACCESS_KEY',
],
]);
$bucket = 'your-bucket-name';
$key = 'uploads/your-file.txt';
$cmd = $s3Client->getCommand('PutObject', [
'Bucket' => $bucket,
'Key' => $key,
]);
// Create a pre-signed URL with a 20-minute expiration
$request = $s3Client->createPresignedRequest($cmd, '+20 minutes');
$presignedUrl = (string) $request->getUri();
echo "Upload Pre-Signed URL: {$presignedUrl}\n";
Using AWS SDK for JavaScript
Install the AWS SDK for JavaScript:
1
npm install aws-sdk
Generate a pre-signed URL for uploading:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Import AWS SDK
const AWS = require('aws-sdk');
// Configure AWS
AWS.config.update({
accessKeyId: 'YOUR_AWS_ACCESS_KEY_ID',
secretAccessKey: 'YOUR_AWS_SECRET_ACCESS_KEY',
region: 'us-west-2',
});
// Create S3 client
const s3 = new AWS.S3();
const params = {
Bucket: 'your-bucket-name',
Key: 'uploads/your-file.txt',
Expires: 1200, // 20 minutes
};
// Generate pre-signed URL
s3.getSignedUrl('putObject', params, (err, url) => {
if (err) {
console.error('Error generating pre-signed URL', err);
} else {
console.log('Upload Pre-Signed URL:', url);
}
});
Generating Pre-Signed URLs for Secure Downloads
Using AWS SDK for PHP
Generate a pre-signed URL for downloading:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
// Initialize S3 client as shown earlier
$bucket = 'your-bucket-name';
$key = 'downloads/your-file.txt';
$cmd = $s3Client->getCommand('GetObject', [
'Bucket' => $bucket,
'Key' => $key,
]);
// Create a pre-signed URL with a 20-minute expiration
$request = $s3Client->createPresignedRequest($cmd, '+20 minutes');
$presignedUrl = (string) $request->getUri();
echo "Download Pre-Signed URL: {$presignedUrl}\n";
Using AWS SDK for JavaScript
Generate a pre-signed URL for downloading:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Configure AWS and create S3 client as shown earlier
const params = {
Bucket: 'your-bucket-name',
Key: 'downloads/your-file.txt',
Expires: 1200, // 20 minutes
};
// Generate pre-signed URL
s3.getSignedUrl('getObject', params, (err, url) => {
if (err) {
console.error('Error generating pre-signed URL', err);
} else {
console.log('Download Pre-Signed URL:', url);
}
});
Implementing Secure Uploads
PHP Example
Upload a file using the pre-signed URL:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
$filePath = '/local/path/to/your-file.txt';
$presignedUrl = '...'; // Pre-signed URL obtained earlier
// Initialize cURL
$ch = curl_init($presignedUrl);
curl_setopt($ch, CURLOPT_PUT, true);
$fh = fopen($filePath, 'r');
curl_setopt($ch, CURLOPT_INFILE, $fh);
curl_setopt($ch, CURLOPT_INFILESIZE, filesize($filePath));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// Execute the request
$response = curl_exec($ch);
if ($response === false) {
throw new Exception('cURL error: ' . curl_error($ch));
}
curl_close($ch);
fclose($fh);
JavaScript Example
Upload a file using the Fetch API:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Get the pre-signed URL
const presignedUrl = '...'; // Pre-signed URL obtained earlier
// Assuming 'fileInput' is an <input type="file"> element
const file = document.getElementById('fileInput').files[0];
fetch(presignedUrl, {
method: 'PUT',
headers: {
'Content-Type': file.type,
},
body: file,
})
.then(response => {
if (response.ok) {
console.log('Upload successful');
} else {
console.error('Upload failed', response.statusText);
}
})
.catch(error => {
console.error('Error:', error);
});
Implementing Secure Downloads
PHP Example
Download a file using the pre-signed URL:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
$presignedUrl = '...'; // Pre-signed URL obtained earlier
// Use file_get_contents
$content = file_get_contents($presignedUrl);
if ($content === false) {
throw new Exception('Failed to download file.');
}
// Save to local file
file_put_contents('/local/path/to/save/your-file.txt', $content);
JavaScript Example
Download a file and trigger a download in the browser:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Get the pre-signed URL
const presignedUrl = '...'; // Pre-signed URL obtained earlier
fetch(presignedUrl)
.then(response => response.blob())
.then(blob => {
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = downloadUrl;
a.download = 'your-file.txt';
document.body.appendChild(a);
a.click();
a.remove();
URL.revokeObjectURL(downloadUrl);
})
.catch(error => {
console.error('Error downloading file:', error);
});
Best Practices for Using Pre-Signed URLs
- Minimal Expiration Time: Set the shortest practical expiration time to reduce the window of opportunity for misuse.
- Use HTTPS: Always use HTTPS URLs to prevent interception of the URL.
- Restrict Permissions: Ensure that the IAM policies for the user generating the pre-signed URLs are as restrictive as possible.
- Monitor Usage: Implement logging and monitoring to detect any unauthorized access or usage patterns.
- Secure Storage: Never expose your AWS credentials in client-side code or public repositories.
Conclusion
AWS S3 pre-signed URLs offer a secure and efficient way to manage temporary access to your S3 objects. By integrating these URLs into your applications, you can facilitate secure uploads and downloads without compromising your AWS credentials or S3 bucket policies.
The code examples provided in PHP and JavaScript should serve as a solid foundation for implementing pre-signed URLs in your projects. Always adhere to best practices to maintain the security and integrity of your data.