SlideShare una empresa de Scribd logo
AWS User Group: Dublin Meetup / 2022-11-17
1
META_SLIDE!
fth.link/presign
loige 2
😎"I have a startup idea..."
loige
fth.link/presign
3
loige
meower
fth.link/presign
4
loige 5
loige 6
loige 7
loige
WTF?
8
Yes, I still have to implement the
profile picture upload feature... 😅
loige 9
$ ~ whoami
👋I'm Luciano ( 🍕🍝 )
Senior Architect @ fourTheorem (Dublin )
nodejsdp.link
📔Co-Author of Node.js Design Patterns 👉
Let's connect!
(blog)
(twitter)
(twitch)
(github)
loige.co
@loige
loige
lmammino 10
Always re-imagining
We are a pioneering technology consultancy
focused on AWS and serverless
| |
Accelerated Serverless AI as a Service Platform Modernisation
loige
✉Reach out to us at
😇We are always looking for talent:
hello@fourTheorem.com
fth.link/careers
11
We host a weekly podcast about AWS
awsbites.com
loige 12
🤔
"upload" feature
in a web app...
loige 13
What's an upload, really?
loige 14
OK, what protocol?
loige 15
loige
Structure of an HTTP request
POST /profilepic/upload HTTP/1.1
Host: api.meower.com
Content-Type: text/plain
Content-Length: 9
Some data
Method
Path Version
Headers
Body
16
loige
What if it's a binary (like a picture)?
PUT /profilepic/upload HTTP/1.1
Host: api.meower.com
Content-Type: image/jpeg
Content-Length: 2097852
����JFIFHH������"��
���Dl��FW�'6N�()H�'p��FD3 [...]
read 2097852
bytes
17
loige
Using Lambda
Lambda proxy integration*
* JSON Based protocol mapping to HTTP
(JSON Request / JSON Response)
18
{
"resource": "/profilepic/upload",
"path": "/profilepic/upload",
"httpMethod": "POST",
"headers": {
"Content-Type": "text/plain",
"Content-Length": "9",
"Host": "api.meower.com",
},
"body": "Some data"
}
1
2
3
4
5
6
7
8
9
10
11
"resource": "/profilepic/upload",
"path": "/profilepic/upload",
{
1
2
3
"httpMethod": "POST",
4
"headers": {
5
"Content-Type": "text/plain",
6
"Content-Length": "9",
7
"Host": "api.meower.com",
8
},
9
"body": "Some data"
10
}
11
"httpMethod": "POST",
{
1
"resource": "/profilepic/upload",
2
"path": "/profilepic/upload",
3
4
"headers": {
5
"Content-Type": "text/plain",
6
"Content-Length": "9",
7
"Host": "api.meower.com",
8
},
9
"body": "Some data"
10
}
11
"headers": {
"Content-Type": "text/plain",
"Content-Length": "9",
"Host": "api.meower.com",
},
{
1
"resource": "/profilepic/upload",
2
"path": "/profilepic/upload",
3
"httpMethod": "POST",
4
5
6
7
8
9
"body": "Some data"
10
}
11
"body": "Some data"
{
1
"resource": "/profilepic/upload",
2
"path": "/profilepic/upload",
3
"httpMethod": "POST",
4
"headers": {
5
"Content-Type": "text/plain",
6
"Content-Length": "9",
7
"Host": "api.meower.com",
8
},
9
10
}
11
{
"resource": "/profilepic/upload",
"path": "/profilepic/upload",
"httpMethod": "POST",
"headers": {
"Content-Type": "text/plain",
"Content-Length": "9",
"Host": "api.meower.com",
},
"body": "Some data"
}
1
2
3
4
5
6
7
8
9
10
11
loige
Lambda proxy integration (request)
19
{
"resource": "/profilepic/upload",
"path": "/profilepic/upload",
"httpMethod": "PUT",
"headers": {
"Content-Type": "image/jpeg",
"Content-Length": "2097852",
"Host": "api.meower.com",
},
"body": "????????"
}
1
2
3
4
5
6
7
8
9
10
11
"Content-Type": "image/jpeg",
"Content-Length": "2097852",
{
1
"resource": "/profilepic/upload",
2
"path": "/profilepic/upload",
3
"httpMethod": "PUT",
4
"headers": {
5
6
7
"Host": "api.meower.com",
8
},
9
"body": "????????"
10
}
11
"body": "????????"
{
1
"resource": "/profilepic/upload",
2
"path": "/profilepic/upload",
3
"httpMethod": "PUT",
4
"headers": {
5
"Content-Type": "image/jpeg",
6
"Content-Length": "2097852",
7
"Host": "api.meower.com",
8
},
9
10
}
11
{
"resource": "/profilepic/upload",
"path": "/profilepic/upload",
"httpMethod": "PUT",
"headers": {
"Content-Type": "image/jpeg",
"Content-Length": "2097852",
"Host": "api.meower.com",
},
"body": "????????"
}
1
2
3
4
5
6
7
8
9
10
11
loige
Lambda proxy integration (request - picture)
20
"isBase64Encoded": true,
"body": "/9j/4AAQSkZJRgABAQEASABIAAD/2w[...]"
{
1
"resource": "/profilepic/upload",
2
"path": "/profilepic/upload",
3
"httpMethod": "PUT",
4
"headers": {
5
"Content-Type": "image/jpeg",
6
"Content-Length": "2097852",
7
"Host": "api.meower.com",
8
},
9
10
11
}
12
{
"resource": "/profilepic/upload",
"path": "/profilepic/upload",
"httpMethod": "PUT",
"headers": {
"Content-Type": "image/jpeg",
"Content-Length": "2097852",
"Host": "api.meower.com",
},
"isBase64Encoded": true,
"body": "/9j/4AAQSkZJRgABAQEASABIAAD/2w[...]"
}
1
2
3
4
5
6
7
8
9
10
11
12
loige
Lambda proxy integration (request - picture)
21
loige
1. Parse request (JSON)
2. Decode body (Base64)
3. Validation / resize
4. ...
Lambda proxy integration request
/profilepic/upload
22
loige
1. Parse request (JSON)
2. Decode body (Base64)
3. Validation / resize
4. ...
Lambda proxy integration request
/profilepic/upload
😎 23
loige
Is this a good solution? 🙄
24
loige
Limitations...
API Gateway requests timeout: 30 sec
API Gateway payload: max 10 MB
Lambda timeout: max 15 mins
Lambda payload size: max 6 MB
Upload: 6 MB / 30 sec
25
loige
Is this a good solution? 🙄
... not really!
What about supporting big images or even videos?
26
loige
An alternative approach
✅ Long lived connection
✅ No size limit
27
loige
🤔SERVERS...
28
loige
S3 pre-signed URLs 😱
An S3 built-in feature
to authorize operations (download, upload, etc) on a
bucket / object
using time-limited authenticated URLs
29
loige
Using S3 pre-signed URLs for upload
* yeah, this can be a Lambda as well 😇
*
30
loige
Using S3 pre-signed URLs for upload
* yeah, this can be a Lambda as well 😇
*
31
loige
Using S3 pre-signed URLs for upload
* yeah, this can be a Lambda as well 😇
*
32
loige
Using S3 pre-signed URLs for upload
* yeah, this can be a Lambda as well 😇
*
33
loige
Using S3 pre-signed URLs for upload
✅
* yeah, this can be a Lambda as well 😇
*
34
loige
... and we can also use it for downloads! 🤩
35
loige
Using S3 pre-signed URLs for download
36
loige
Using S3 pre-signed URLs for download
37
loige
Using S3 pre-signed URLs for download
38
loige
Using S3 pre-signed URLs for download
39
loige
Using S3 pre-signed URLs for download
✅
40
loige
⚠VERY important details!
I lied to you a little in those diagrams... 🤥
It's a decent mental model, but it's not accurate 😅
The server never really talks with S3!
The server actually creates the signed URL by itself!
We will see later what's the security model around this idea!
41
loige
Is this a good solution? 🙄
✅It's a managed feature (a.k.a. no servers to manage)
✅We can upload and download arbitrarily big files with no practical limits*
✅Reasonably simple and secure
👍Seems good to me!
* objects in S3 are "limited" to 5TB (when using multi-part upload), 5 GB otherwise.
42
loige
Generating our first pre-signed URL
$ aws s3 presign 
s3://finance-department-bucket/2022/tax-certificate.pdf
https://s3.amazonaws.com/finance-department-bucket/2022/tax-
certificate.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-
Credential=AKIA3SGQVQG7FGA6KKA6%2F20221104%2Fus-east-
1%2Fs3%2Faws4_request&X-Amz-Date=20221104T140227Z&X-Amz-
Expires=3600&X-Amz-SignedHeaders=host&X-Amz-
Signature=b228dbec8c1008c80c162e1210e4503dceead1e4d4751b4d9787
314fd6da4d55
Whoever has this URL can
download the tax certificate!
43
loige
What's in a pre-signed URL
https://s3.amazonaws.com/finance-department-bucket/2022/tax-
certificate.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-
Credential=AKIA3SGQVQG7FGA6KKA6%2F20221104%2Fus-east-
1%2Fs3%2Faws4_request&X-Amz-Date=20221104T140227Z&X-Amz-
Expires=3600&X-Amz-SignedHeaders=host&X-Amz-
Signature=b228dbec8c1008c80c162e1210e4503dceead1e4d4751b4d9787
314fd6da4d55
44
loige
What's in a pre-signed URL
https://s3.amazonaws.com
/finance-department-bucket
/2022/tax-certificate.pdf
?X-Amz-Algorithm=AWS4-HMAC-SHA256
&X-Amz-Credential=AKIA3SGQXQG7XXXYKKA6%2F20221104...
&X-Amz-Date=20221104T140227Z
&X-Amz-Expires=3600
&X-Amz-SignedHeaders=host
&X-Amz-Signature=b228dbec8c1008c80c162e1210e4503dceead1e4d4...
What if I change this to
/passwords.txt?
45
loige
👿46
loige
Pre-signed URLs validation
https://s3.amazonaws.com
/finance-department-bucket
/2022/tax-certificate.pdf
?X-Amz-Algorithm=AWS4-HMAC-SHA256
&X-Amz-Credential=AKIA3SGQXQG7XXXYKKA6%2F20221104...
&X-Amz-Date=20221104T140227Z
&X-Amz-Expires=3600
&X-Amz-SignedHeaders=host
&X-Amz-Signature=b228dbec8c1008c80c162e1210e4503dceead1e4d4...
47
loige
🤓
Once a pre-signed URL is generated you
cannot edit it without breaking it
Photo by on
CHUTTERSNAP Unsplash
⚠Also note that you can use a pre-signed URL as many
times as you want until it expires
48
loige
🔐Permissions
Anyone with valid credentials can create a pre-signed URL (client side)
valid credentials = Role, User, or Security Token
The generated URL inherits the permissions of the credentials used to generate it
This means you can generate pre-signed URLs for things you don't have access to 😅
49
loige
$ aws s3 presign s3://ireland/i-love-you
https://ireland.s3.eu-west-1.amazonaws.com/i-love-you?X-Amz-
Algorithm=AWS4-HMAC-SHA256&X-Amz-
Credential=AKIA3ABCVQG7FGA6KKA6%2F20221115%2Feu-west-
1%2Fs3%2Faws4_request&X-Amz-Date=20221115T182036Z&X-Amz-
Expires=3600&X-Amz-SignedHeaders=host&X-Amz-
Signature=75749c92d94d03e411e7bbf64419f2af09301d1791b0df54c639
137c715f7888
😱
I swear I don't even know if this
bucket exists or who owns it!
50
loige
Pre-signed URLs are validated at request time
51
loige
Creating a pre-signed URL
programmatically
AWS SDK for JavaScript v3
52
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
const s3Client = new S3Client()
const command = new GetObjectCommand({
Bucket: "some-bucket",
Key: "some-object"
})
const preSignedUrl = await getSignedUrl(s3Client, command, {
expiresIn: 3600
})
console.log(preSignedUrl)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
1
2
3
const s3Client = new S3Client()
4
5
const command = new GetObjectCommand({
6
Bucket: "some-bucket",
7
Key: "some-object"
8
})
9
10
const preSignedUrl = await getSignedUrl(s3Client, command, {
11
expiresIn: 3600
12
})
13
14
console.log(preSignedUrl)
15
const s3Client = new S3Client()
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'
1
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
2
3
4
5
const command = new GetObjectCommand({
6
Bucket: "some-bucket",
7
Key: "some-object"
8
})
9
10
const preSignedUrl = await getSignedUrl(s3Client, command, {
11
expiresIn: 3600
12
})
13
14
console.log(preSignedUrl)
15
const command = new GetObjectCommand({
Bucket: "some-bucket",
Key: "some-object"
})
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'
1
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
2
3
const s3Client = new S3Client()
4
5
6
7
8
9
10
const preSignedUrl = await getSignedUrl(s3Client, command, {
11
expiresIn: 3600
12
})
13
14
console.log(preSignedUrl)
15
const preSignedUrl = await getSignedUrl(s3Client, command, {
expiresIn: 3600
})
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'
1
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
2
3
const s3Client = new S3Client()
4
5
const command = new GetObjectCommand({
6
Bucket: "some-bucket",
7
Key: "some-object"
8
})
9
10
11
12
13
14
console.log(preSignedUrl)
15 console.log(preSignedUrl)
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'
1
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
2
3
const s3Client = new S3Client()
4
5
const command = new GetObjectCommand({
6
Bucket: "some-bucket",
7
Key: "some-object"
8
})
9
10
const preSignedUrl = await getSignedUrl(s3Client, command, {
11
expiresIn: 3600
12
})
13
14
15
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
const s3Client = new S3Client()
const command = new GetObjectCommand({
Bucket: "some-bucket",
Key: "some-object"
})
const preSignedUrl = await getSignedUrl(s3Client, command, {
expiresIn: 3600
})
console.log(preSignedUrl)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
loige 53
loige
📦
Uploading a file
using pre-signed URLs
54
loige
2 Options: PUT & POST 🤨
55
loige
PUT Method
PUT <preSignedURL> HTTP/1.1
Host: <bucket>.s3.<region>.amazonaws.com
Content-Length: 2097852
����JFIFHH������"��
���Dl��FW�'6N�()H�'p��FD3 [...]
56
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
const s3Client = new S3Client()
const command = new PutObjectCommand({
Bucket: "some-bucket",
Key: "some-object"
})
const preSignedUrl = await getSignedUrl(s3Client, command, {
expiresIn: 3600
})
console.log(preSignedUrl)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
loige
Only difference with the
previous example
57
loige
PUT Method - Limitations
You cannot set a limit on the upload size (max of 5 GB)! *
You can limit the Content-Type but you can specify exactly one
* Unless you know the exact size in advance
58
loige
POST method
It uses the multipart/form-data encoding (form upload)
Gives more freedom to the client to shape the request (Content-Type, file name, etc)
It uses a policy mechanism to define the "rules" of what can be uploaded
E.g. you can limit the supported mime types and provide a maximum file size
You can use it to upload from a web form and even configure the redirect URL
It's not really a URL but more of a pre-signed form!
59
POST / HTTP/1.1
Host: <bucket>.s3.amazonaws.com
Content-Type: multipart/form-data; boundary=9431149156168
Content-Length: 2097852
--9431149156168
Content-Disposition: form-data; name="key"
picture.jpg
--9431149156168
Content-Disposition: form-data; name="X-Amz-Credential"
AKIA3SGABCDXXXA6KKA6/20221115/eu-west-1/s3/aws4_request
--9431149156168
Content-Disposition: form-data; name="Policy"
eyJleHBpcmF0aW9uIjoiMjAyMi0xMS0xNVQyMDo0NjozN1oiLCJjb25kaXRpb25zIjpbWyJj[...]
--9431149156168
Content-Disposition: form-data; name="X-Amz-Signature"
2c1da0001dfec7caea1c9fb80c7bc8847f515a9e4483d2942464f48d2f827de7
--9431149156168
Content-Disposition: form-data; name="file"; filename="MyFilename.jpg"
Content-Type: image/jpeg
����JFIFHH������"��
���Dl��FW�'6N�()H�'p��FD3[...]
--9431149156168--
loige
60
loige
POST method Policy
A JSON object (Base64 encoded) that defines the upload rules (conditions) and the
expiration date
This is what gets signed: you cannot alter the policy without breaking the signature
{
"expiration": "2022-11-15T20:46:37Z",
"conditions": [
["content-length-range", 0, 5242880],
["starts-with", "$Content-Type", "image/"],
{"bucket": "somebucket"},
{"X-Amz-Algorithm": "AWS4-HMAC-SHA256"},
{"X-Amz-Credential": "AKIA3SGABCDXXXA6KKA6/20221115/eu-west-1/s3/aws4_request"},
{"X-Amz-Date": "20221115T194637Z"},
{"key": "picture.jpg"}
]
}
61
import { S3Client } from '@aws-sdk/client-s3'
import { createPresignedPost } from '@aws-sdk/s3-presigned-post'
const { BUCKET_NAME, OBJECT_KEY } = process.env
const s3Client = new S3Client()
const { url, fields } = await createPresignedPost(s3Client, {
Bucket: 'somebucket',
Key: 'someobject',
Conditions: [
['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max
],
Fields: {
success_action_status: '201',
'Content-Type': 'image/png'
},
Expires: 3600
})
console.log({ url, fields })
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { createPresignedPost } from '@aws-sdk/s3-presigned-post'
import { S3Client } from '@aws-sdk/client-s3'
1
2
3
const { BUCKET_NAME, OBJECT_KEY } = process.env
4
const s3Client = new S3Client()
5
6
const { url, fields } = await createPresignedPost(s3Client, {
7
Bucket: 'somebucket',
8
Key: 'someobject',
9
Conditions: [
10
['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max
11
],
12
Fields: {
13
success_action_status: '201',
14
'Content-Type': 'image/png'
15
},
16
Expires: 3600
17
})
18
19
console.log({ url, fields })
20
const { url, fields } = await createPresignedPost(s3Client, {
})
import { S3Client } from '@aws-sdk/client-s3'
1
import { createPresignedPost } from '@aws-sdk/s3-presigned-post'
2
3
const { BUCKET_NAME, OBJECT_KEY } = process.env
4
const s3Client = new S3Client()
5
6
7
Bucket: 'somebucket',
8
Key: 'someobject',
9
Conditions: [
10
['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max
11
],
12
Fields: {
13
success_action_status: '201',
14
'Content-Type': 'image/png'
15
},
16
Expires: 3600
17
18
19
console.log({ url, fields })
20
Bucket: 'somebucket',
import { S3Client } from '@aws-sdk/client-s3'
1
import { createPresignedPost } from '@aws-sdk/s3-presigned-post'
2
3
const { BUCKET_NAME, OBJECT_KEY } = process.env
4
const s3Client = new S3Client()
5
6
const { url, fields } = await createPresignedPost(s3Client, {
7
8
Key: 'someobject',
9
Conditions: [
10
['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max
11
],
12
Fields: {
13
success_action_status: '201',
14
'Content-Type': 'image/png'
15
},
16
Expires: 3600
17
})
18
19
console.log({ url, fields })
20
Key: 'someobject',
import { S3Client } from '@aws-sdk/client-s3'
1
import { createPresignedPost } from '@aws-sdk/s3-presigned-post'
2
3
const { BUCKET_NAME, OBJECT_KEY } = process.env
4
const s3Client = new S3Client()
5
6
const { url, fields } = await createPresignedPost(s3Client, {
7
Bucket: 'somebucket',
8
9
Conditions: [
10
['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max
11
],
12
Fields: {
13
success_action_status: '201',
14
'Content-Type': 'image/png'
15
},
16
Expires: 3600
17
})
18
19
console.log({ url, fields })
20
Conditions: [
['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max
],
import { S3Client } from '@aws-sdk/client-s3'
1
import { createPresignedPost } from '@aws-sdk/s3-presigned-post'
2
3
const { BUCKET_NAME, OBJECT_KEY } = process.env
4
const s3Client = new S3Client()
5
6
const { url, fields } = await createPresignedPost(s3Client, {
7
Bucket: 'somebucket',
8
Key: 'someobject',
9
10
11
12
Fields: {
13
success_action_status: '201',
14
'Content-Type': 'image/png'
15
},
16
Expires: 3600
17
})
18
19
console.log({ url, fields })
20
Fields: {
success_action_status: '201',
'Content-Type': 'image/png'
},
import { S3Client } from '@aws-sdk/client-s3'
1
import { createPresignedPost } from '@aws-sdk/s3-presigned-post'
2
3
const { BUCKET_NAME, OBJECT_KEY } = process.env
4
const s3Client = new S3Client()
5
6
const { url, fields } = await createPresignedPost(s3Client, {
7
Bucket: 'somebucket',
8
Key: 'someobject',
9
Conditions: [
10
['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max
11
],
12
13
14
15
16
Expires: 3600
17
})
18
19
console.log({ url, fields })
20
Expires: 3600
import { S3Client } from '@aws-sdk/client-s3'
1
import { createPresignedPost } from '@aws-sdk/s3-presigned-post'
2
3
const { BUCKET_NAME, OBJECT_KEY } = process.env
4
const s3Client = new S3Client()
5
6
const { url, fields } = await createPresignedPost(s3Client, {
7
Bucket: 'somebucket',
8
Key: 'someobject',
9
Conditions: [
10
['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max
11
],
12
Fields: {
13
success_action_status: '201',
14
'Content-Type': 'image/png'
15
},
16
17
})
18
19
console.log({ url, fields })
20 console.log({ url, fields })
import { S3Client } from '@aws-sdk/client-s3'
1
import { createPresignedPost } from '@aws-sdk/s3-presigned-post'
2
3
const { BUCKET_NAME, OBJECT_KEY } = process.env
4
const s3Client = new S3Client()
5
6
const { url, fields } = await createPresignedPost(s3Client, {
7
Bucket: 'somebucket',
8
Key: 'someobject',
9
Conditions: [
10
['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max
11
],
12
Fields: {
13
success_action_status: '201',
14
'Content-Type': 'image/png'
15
},
16
Expires: 3600
17
})
18
19
20
import { S3Client } from '@aws-sdk/client-s3'
import { createPresignedPost } from '@aws-sdk/s3-presigned-post'
const { BUCKET_NAME, OBJECT_KEY } = process.env
const s3Client = new S3Client()
const { url, fields } = await createPresignedPost(s3Client, {
Bucket: 'somebucket',
Key: 'someobject',
Conditions: [
['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max
],
Fields: {
success_action_status: '201',
'Content-Type': 'image/png'
},
Expires: 3600
})
console.log({ url, fields })
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
loige 62
// you can use `url` and `fields` to generate an HTML form
const code = `<h1>Upload an image to S3</h1>
<form action="${url}" method="post" enctype="multipart/form-data">
${Object.entries(fields).map(([key, value]) => {
return `<input type="hidden" name="${key}" value="${value.replace(/"/g, '&quot;')}">`
}).join('n')}
<div><input type="file" name="file" accept="image/png"></div>
<div><input type="submit" value="Upload"></div>
</form>`
1
2
3
4
5
6
7
8
9
10
loige 63
loige
Limitations and quirks
It supports only 1 file (cannot upload multiple files in one go)
The file field must be the last entry in the form
(S3 will ignore every other field after the file)
From the browser (AJAX) you need to enable CORS on the bucket
64
loige
Should I use PUT or POST? 🧐
PUT is simpler but definitely more limited
POST is slightly more complicated (and less adopted) but it's more flexible
You should probably put some time into learning POST and use that!
65
loige
Pre-signed URLs for other operations
S3 pre-signed URLs are not limited to GET, PUT or POST operations
You can literally create pre-signed URLs for any command
(DeleteObject, ListBuckets, MultiPartUpload, etc...)
66
loige
Do you need moar examples? 😼
github.com/lmammino/s3-presigned-urls-examples
67
loige
... In summary
S3 pre-signed URLs are a great way to authorise operations on S3
They are generally used to implement upload/download features
The signature is created client-side so you can sign anything. Access is validated at
request time
This is not the only solution, you can also use the JavaScript SDK from the frontend
and get limited credentials from Cognito (Amplify makes that process simpler)
For upload you can use PUT and POST, but POST is much more flexible
💬PS: Meower.com doesn't really exist... but... do you want to invest?! It's a great
idea, trust me!
68
Cover photo by on
Kelly Sikkema Unsplash
fourtheorem.com
THANKS! 🙌
fth.link/presign
loige
It's a wrap!
69

Más contenido relacionado

La actualidad más candente

성능 최대화를 위한 CloudFront 설정 Best Practice
성능 최대화를 위한 CloudFront 설정 Best Practice성능 최대화를 위한 CloudFront 설정 Best Practice
성능 최대화를 위한 CloudFront 설정 Best Practice
GS Neotek
 
Introduction XSS
Introduction XSSIntroduction XSS
Introduction XSS
Aymeric Lagier
 
전투 시스템 기획(Canvas 스터디 1차)
전투 시스템 기획(Canvas 스터디 1차)전투 시스템 기획(Canvas 스터디 1차)
전투 시스템 기획(Canvas 스터디 1차)
Chanman Jo
 
Amazon Cognito와 함께 서버리스를..! - 이재일 (강남비기너모임) :: AWS Community Day 2017
Amazon Cognito와 함께 서버리스를..! - 이재일 (강남비기너모임) :: AWS Community Day 2017Amazon Cognito와 함께 서버리스를..! - 이재일 (강남비기너모임) :: AWS Community Day 2017
Amazon Cognito와 함께 서버리스를..! - 이재일 (강남비기너모임) :: AWS Community Day 2017
AWSKRUG - AWS한국사용자모임
 
多要素認証による Amazon WorkSpaces の利用
多要素認証による Amazon WorkSpaces の利用多要素認証による Amazon WorkSpaces の利用
多要素認証による Amazon WorkSpaces の利用
Amazon Web Services Japan
 
Webinar: Working with Graph Data in MongoDB
Webinar: Working with Graph Data in MongoDBWebinar: Working with Graph Data in MongoDB
Webinar: Working with Graph Data in MongoDB
MongoDB
 
엄재민 Nhn과제 신규 게임 컨셉 제안서
엄재민 Nhn과제 신규 게임 컨셉 제안서엄재민 Nhn과제 신규 게임 컨셉 제안서
엄재민 Nhn과제 신규 게임 컨셉 제안서
재민 엄
 
전형규, 좋은 이름, 나쁜 이름, 이상한 이름, NDC2018
전형규, 좋은 이름, 나쁜 이름, 이상한 이름, NDC2018전형규, 좋은 이름, 나쁜 이름, 이상한 이름, NDC2018
전형규, 좋은 이름, 나쁜 이름, 이상한 이름, NDC2018
devCAT Studio, NEXON
 
AWS 클라우드 기반 확장성 높은 천만 사용자 웹 서비스 만들기 - 윤석찬
AWS 클라우드 기반 확장성 높은 천만 사용자 웹 서비스 만들기 - 윤석찬AWS 클라우드 기반 확장성 높은 천만 사용자 웹 서비스 만들기 - 윤석찬
AWS 클라우드 기반 확장성 높은 천만 사용자 웹 서비스 만들기 - 윤석찬
Amazon Web Services Korea
 
[Gaming on AWS] 넥슨 - AWS를 활용한 모바일 게임 서버 개발: 퍼즐 주주의 사례
[Gaming on AWS] 넥슨 - AWS를 활용한 모바일 게임 서버 개발: 퍼즐 주주의 사례[Gaming on AWS] 넥슨 - AWS를 활용한 모바일 게임 서버 개발: 퍼즐 주주의 사례
[Gaming on AWS] 넥슨 - AWS를 활용한 모바일 게임 서버 개발: 퍼즐 주주의 사례
Amazon Web Services Korea
 
API Abuse - The Anatomy of An Attack
API Abuse -  The Anatomy of An AttackAPI Abuse -  The Anatomy of An Attack
API Abuse - The Anatomy of An Attack
Nordic APIs
 
AWS Lambda 내부 동작 방식 및 활용 방법 자세히 살펴 보기 - 김일호 솔루션즈 아키텍트 매니저, AWS :: AWS Summit ...
AWS Lambda 내부 동작 방식 및 활용 방법 자세히 살펴 보기 - 김일호 솔루션즈 아키텍트 매니저, AWS :: AWS Summit ...AWS Lambda 내부 동작 방식 및 활용 방법 자세히 살펴 보기 - 김일호 솔루션즈 아키텍트 매니저, AWS :: AWS Summit ...
AWS Lambda 내부 동작 방식 및 활용 방법 자세히 살펴 보기 - 김일호 솔루션즈 아키텍트 매니저, AWS :: AWS Summit ...
Amazon Web Services Korea
 
오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...
오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...
오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...
Amazon Web Services Korea
 
Mongodb basics and architecture
Mongodb basics and architectureMongodb basics and architecture
Mongodb basics and architecture
Bishal Khanal
 
Python과 Git으로 만드는 모바일 게임 패치 시스템
Python과 Git으로 만드는 모바일 게임 패치 시스템Python과 Git으로 만드는 모바일 게임 패치 시스템
Python과 Git으로 만드는 모바일 게임 패치 시스템
Youngtaek Oh
 
MongoDB
MongoDBMongoDB
MongoDB
nikhil2807
 
Attacking AWS: the full cyber kill chain
Attacking AWS: the full cyber kill chainAttacking AWS: the full cyber kill chain
Attacking AWS: the full cyber kill chain
SecuRing
 
Route53 및 CloudFront를 이용한 CDN 활용기 - AWS Summit Seoul 2017
Route53 및 CloudFront를 이용한 CDN 활용기 - AWS Summit Seoul 2017Route53 및 CloudFront를 이용한 CDN 활용기 - AWS Summit Seoul 2017
Route53 및 CloudFront를 이용한 CDN 활용기 - AWS Summit Seoul 2017
Amazon Web Services Korea
 
게임서버 구축 방법비교 : GBaaS vs. Self-hosting
게임서버 구축 방법비교 : GBaaS vs. Self-hosting게임서버 구축 방법비교 : GBaaS vs. Self-hosting
게임서버 구축 방법비교 : GBaaS vs. Self-hosting
iFunFactory Inc.
 
Count min sketch
Count min sketchCount min sketch
Count min sketch
DaeMyung Kang
 

La actualidad más candente (20)

성능 최대화를 위한 CloudFront 설정 Best Practice
성능 최대화를 위한 CloudFront 설정 Best Practice성능 최대화를 위한 CloudFront 설정 Best Practice
성능 최대화를 위한 CloudFront 설정 Best Practice
 
Introduction XSS
Introduction XSSIntroduction XSS
Introduction XSS
 
전투 시스템 기획(Canvas 스터디 1차)
전투 시스템 기획(Canvas 스터디 1차)전투 시스템 기획(Canvas 스터디 1차)
전투 시스템 기획(Canvas 스터디 1차)
 
Amazon Cognito와 함께 서버리스를..! - 이재일 (강남비기너모임) :: AWS Community Day 2017
Amazon Cognito와 함께 서버리스를..! - 이재일 (강남비기너모임) :: AWS Community Day 2017Amazon Cognito와 함께 서버리스를..! - 이재일 (강남비기너모임) :: AWS Community Day 2017
Amazon Cognito와 함께 서버리스를..! - 이재일 (강남비기너모임) :: AWS Community Day 2017
 
多要素認証による Amazon WorkSpaces の利用
多要素認証による Amazon WorkSpaces の利用多要素認証による Amazon WorkSpaces の利用
多要素認証による Amazon WorkSpaces の利用
 
Webinar: Working with Graph Data in MongoDB
Webinar: Working with Graph Data in MongoDBWebinar: Working with Graph Data in MongoDB
Webinar: Working with Graph Data in MongoDB
 
엄재민 Nhn과제 신규 게임 컨셉 제안서
엄재민 Nhn과제 신규 게임 컨셉 제안서엄재민 Nhn과제 신규 게임 컨셉 제안서
엄재민 Nhn과제 신규 게임 컨셉 제안서
 
전형규, 좋은 이름, 나쁜 이름, 이상한 이름, NDC2018
전형규, 좋은 이름, 나쁜 이름, 이상한 이름, NDC2018전형규, 좋은 이름, 나쁜 이름, 이상한 이름, NDC2018
전형규, 좋은 이름, 나쁜 이름, 이상한 이름, NDC2018
 
AWS 클라우드 기반 확장성 높은 천만 사용자 웹 서비스 만들기 - 윤석찬
AWS 클라우드 기반 확장성 높은 천만 사용자 웹 서비스 만들기 - 윤석찬AWS 클라우드 기반 확장성 높은 천만 사용자 웹 서비스 만들기 - 윤석찬
AWS 클라우드 기반 확장성 높은 천만 사용자 웹 서비스 만들기 - 윤석찬
 
[Gaming on AWS] 넥슨 - AWS를 활용한 모바일 게임 서버 개발: 퍼즐 주주의 사례
[Gaming on AWS] 넥슨 - AWS를 활용한 모바일 게임 서버 개발: 퍼즐 주주의 사례[Gaming on AWS] 넥슨 - AWS를 활용한 모바일 게임 서버 개발: 퍼즐 주주의 사례
[Gaming on AWS] 넥슨 - AWS를 활용한 모바일 게임 서버 개발: 퍼즐 주주의 사례
 
API Abuse - The Anatomy of An Attack
API Abuse -  The Anatomy of An AttackAPI Abuse -  The Anatomy of An Attack
API Abuse - The Anatomy of An Attack
 
AWS Lambda 내부 동작 방식 및 활용 방법 자세히 살펴 보기 - 김일호 솔루션즈 아키텍트 매니저, AWS :: AWS Summit ...
AWS Lambda 내부 동작 방식 및 활용 방법 자세히 살펴 보기 - 김일호 솔루션즈 아키텍트 매니저, AWS :: AWS Summit ...AWS Lambda 내부 동작 방식 및 활용 방법 자세히 살펴 보기 - 김일호 솔루션즈 아키텍트 매니저, AWS :: AWS Summit ...
AWS Lambda 내부 동작 방식 및 활용 방법 자세히 살펴 보기 - 김일호 솔루션즈 아키텍트 매니저, AWS :: AWS Summit ...
 
오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...
오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...
오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...
 
Mongodb basics and architecture
Mongodb basics and architectureMongodb basics and architecture
Mongodb basics and architecture
 
Python과 Git으로 만드는 모바일 게임 패치 시스템
Python과 Git으로 만드는 모바일 게임 패치 시스템Python과 Git으로 만드는 모바일 게임 패치 시스템
Python과 Git으로 만드는 모바일 게임 패치 시스템
 
MongoDB
MongoDBMongoDB
MongoDB
 
Attacking AWS: the full cyber kill chain
Attacking AWS: the full cyber kill chainAttacking AWS: the full cyber kill chain
Attacking AWS: the full cyber kill chain
 
Route53 및 CloudFront를 이용한 CDN 활용기 - AWS Summit Seoul 2017
Route53 및 CloudFront를 이용한 CDN 활용기 - AWS Summit Seoul 2017Route53 및 CloudFront를 이용한 CDN 활용기 - AWS Summit Seoul 2017
Route53 및 CloudFront를 이용한 CDN 활용기 - AWS Summit Seoul 2017
 
게임서버 구축 방법비교 : GBaaS vs. Self-hosting
게임서버 구축 방법비교 : GBaaS vs. Self-hosting게임서버 구축 방법비교 : GBaaS vs. Self-hosting
게임서버 구축 방법비교 : GBaaS vs. Self-hosting
 
Count min sketch
Count min sketchCount min sketch
Count min sketch
 

Similar a Everything I know about S3 pre-signed URLs

Building a dev pipeline using GitHub Actions, Node.js, and AWS ECS Fargate
Building a dev pipeline using GitHub Actions, Node.js, and AWS ECS FargateBuilding a dev pipeline using GitHub Actions, Node.js, and AWS ECS Fargate
Building a dev pipeline using GitHub Actions, Node.js, and AWS ECS Fargate
datree
 
Continuous Integration and Deployment Best Practices on AWS (ARC307) | AWS re...
Continuous Integration and Deployment Best Practices on AWS (ARC307) | AWS re...Continuous Integration and Deployment Best Practices on AWS (ARC307) | AWS re...
Continuous Integration and Deployment Best Practices on AWS (ARC307) | AWS re...
Amazon Web Services
 
Continuous Deployment @ AWS Re:Invent
Continuous Deployment @ AWS Re:InventContinuous Deployment @ AWS Re:Invent
Continuous Deployment @ AWS Re:Invent
John Schneider
 
Supercharge your app with Cloud Functions for Firebase
Supercharge your app with Cloud Functions for FirebaseSupercharge your app with Cloud Functions for Firebase
Supercharge your app with Cloud Functions for Firebase
Bret McGowen - NYC Google Developer Advocate
 
There is something about serverless
There is something about serverlessThere is something about serverless
There is something about serverless
gjdevos
 
Serverless in production, an experience report (FullStack 2018)
Serverless in production, an experience report (FullStack 2018)Serverless in production, an experience report (FullStack 2018)
Serverless in production, an experience report (FullStack 2018)
Yan Cui
 
DevSecCon Singapore 2018 - Remove developers’ shameful secrets or simply rem...
DevSecCon Singapore 2018 -  Remove developers’ shameful secrets or simply rem...DevSecCon Singapore 2018 -  Remove developers’ shameful secrets or simply rem...
DevSecCon Singapore 2018 - Remove developers’ shameful secrets or simply rem...
DevSecCon
 
Serverless for High Performance Computing
Serverless for High Performance ComputingServerless for High Performance Computing
Serverless for High Performance Computing
Luciano Mammino
 
SnapyX - ParisJS
SnapyX - ParisJSSnapyX - ParisJS
SnapyX - ParisJS
florianharmel
 
SnapyX
SnapyXSnapyX
SnapyX
ekino
 
[AWS Builders] Effective AWS Glue
[AWS Builders] Effective AWS Glue[AWS Builders] Effective AWS Glue
[AWS Builders] Effective AWS Glue
Amazon Web Services Korea
 
Adopt openjdk and how it impacts you in 2020
Adopt openjdk and how it impacts you in 2020Adopt openjdk and how it impacts you in 2020
Adopt openjdk and how it impacts you in 2020
George Adams
 
Gojko's 5 rules for super responsive Serverless applications
Gojko's 5 rules for super responsive Serverless applicationsGojko's 5 rules for super responsive Serverless applications
Gojko's 5 rules for super responsive Serverless applications
Daniel Zivkovic
 
Serverless in Production, an experience report (AWS UG South Wales)
Serverless in Production, an experience report (AWS UG South Wales)Serverless in Production, an experience report (AWS UG South Wales)
Serverless in Production, an experience report (AWS UG South Wales)
Yan Cui
 
DevSecCon SG 2018 Fabian Presentation Slides
DevSecCon SG 2018 Fabian Presentation SlidesDevSecCon SG 2018 Fabian Presentation Slides
DevSecCon SG 2018 Fabian Presentation Slides
Fab L
 
Lessons learned from a large scale OSGi web app
Lessons learned from a large scale OSGi web appLessons learned from a large scale OSGi web app
Lessons learned from a large scale OSGi web app
Paul Bakker
 
IDEALIZE 2023 - NodeJS & Firebase Session
IDEALIZE 2023 - NodeJS & Firebase SessionIDEALIZE 2023 - NodeJS & Firebase Session
IDEALIZE 2023 - NodeJS & Firebase Session
Brion Mario
 
Function as a Service
Function as a ServiceFunction as a Service
Function as a Service
rich fernandez
 
Serverless for High Performance Computing
Serverless for High Performance ComputingServerless for High Performance Computing
Serverless for High Performance Computing
Luciano Mammino
 
Serverless in production, an experience report
Serverless in production, an experience reportServerless in production, an experience report
Serverless in production, an experience report
Yan Cui
 

Similar a Everything I know about S3 pre-signed URLs (20)

Building a dev pipeline using GitHub Actions, Node.js, and AWS ECS Fargate
Building a dev pipeline using GitHub Actions, Node.js, and AWS ECS FargateBuilding a dev pipeline using GitHub Actions, Node.js, and AWS ECS Fargate
Building a dev pipeline using GitHub Actions, Node.js, and AWS ECS Fargate
 
Continuous Integration and Deployment Best Practices on AWS (ARC307) | AWS re...
Continuous Integration and Deployment Best Practices on AWS (ARC307) | AWS re...Continuous Integration and Deployment Best Practices on AWS (ARC307) | AWS re...
Continuous Integration and Deployment Best Practices on AWS (ARC307) | AWS re...
 
Continuous Deployment @ AWS Re:Invent
Continuous Deployment @ AWS Re:InventContinuous Deployment @ AWS Re:Invent
Continuous Deployment @ AWS Re:Invent
 
Supercharge your app with Cloud Functions for Firebase
Supercharge your app with Cloud Functions for FirebaseSupercharge your app with Cloud Functions for Firebase
Supercharge your app with Cloud Functions for Firebase
 
There is something about serverless
There is something about serverlessThere is something about serverless
There is something about serverless
 
Serverless in production, an experience report (FullStack 2018)
Serverless in production, an experience report (FullStack 2018)Serverless in production, an experience report (FullStack 2018)
Serverless in production, an experience report (FullStack 2018)
 
DevSecCon Singapore 2018 - Remove developers’ shameful secrets or simply rem...
DevSecCon Singapore 2018 -  Remove developers’ shameful secrets or simply rem...DevSecCon Singapore 2018 -  Remove developers’ shameful secrets or simply rem...
DevSecCon Singapore 2018 - Remove developers’ shameful secrets or simply rem...
 
Serverless for High Performance Computing
Serverless for High Performance ComputingServerless for High Performance Computing
Serverless for High Performance Computing
 
SnapyX - ParisJS
SnapyX - ParisJSSnapyX - ParisJS
SnapyX - ParisJS
 
SnapyX
SnapyXSnapyX
SnapyX
 
[AWS Builders] Effective AWS Glue
[AWS Builders] Effective AWS Glue[AWS Builders] Effective AWS Glue
[AWS Builders] Effective AWS Glue
 
Adopt openjdk and how it impacts you in 2020
Adopt openjdk and how it impacts you in 2020Adopt openjdk and how it impacts you in 2020
Adopt openjdk and how it impacts you in 2020
 
Gojko's 5 rules for super responsive Serverless applications
Gojko's 5 rules for super responsive Serverless applicationsGojko's 5 rules for super responsive Serverless applications
Gojko's 5 rules for super responsive Serverless applications
 
Serverless in Production, an experience report (AWS UG South Wales)
Serverless in Production, an experience report (AWS UG South Wales)Serverless in Production, an experience report (AWS UG South Wales)
Serverless in Production, an experience report (AWS UG South Wales)
 
DevSecCon SG 2018 Fabian Presentation Slides
DevSecCon SG 2018 Fabian Presentation SlidesDevSecCon SG 2018 Fabian Presentation Slides
DevSecCon SG 2018 Fabian Presentation Slides
 
Lessons learned from a large scale OSGi web app
Lessons learned from a large scale OSGi web appLessons learned from a large scale OSGi web app
Lessons learned from a large scale OSGi web app
 
IDEALIZE 2023 - NodeJS & Firebase Session
IDEALIZE 2023 - NodeJS & Firebase SessionIDEALIZE 2023 - NodeJS & Firebase Session
IDEALIZE 2023 - NodeJS & Firebase Session
 
Function as a Service
Function as a ServiceFunction as a Service
Function as a Service
 
Serverless for High Performance Computing
Serverless for High Performance ComputingServerless for High Performance Computing
Serverless for High Performance Computing
 
Serverless in production, an experience report
Serverless in production, an experience reportServerless in production, an experience report
Serverless in production, an experience report
 

Más de Luciano Mammino

Did you know JavaScript has iterators? DublinJS
Did you know JavaScript has iterators? DublinJSDid you know JavaScript has iterators? DublinJS
Did you know JavaScript has iterators? DublinJS
Luciano Mammino
 
What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...
What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...
What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...
Luciano Mammino
 
Building an invite-only microsite with Next.js & Airtable - ReactJS Milano
Building an invite-only microsite with Next.js & Airtable - ReactJS MilanoBuilding an invite-only microsite with Next.js & Airtable - ReactJS Milano
Building an invite-only microsite with Next.js & Airtable - ReactJS Milano
Luciano Mammino
 
From Node.js to Design Patterns - BuildPiper
From Node.js to Design Patterns - BuildPiperFrom Node.js to Design Patterns - BuildPiper
From Node.js to Design Patterns - BuildPiper
Luciano Mammino
 
Let's build a 0-cost invite-only website with Next.js and Airtable!
Let's build a 0-cost invite-only website with Next.js and Airtable!Let's build a 0-cost invite-only website with Next.js and Airtable!
Let's build a 0-cost invite-only website with Next.js and Airtable!
Luciano Mammino
 
JavaScript Iteration Protocols - Workshop NodeConf EU 2022
JavaScript Iteration Protocols - Workshop NodeConf EU 2022JavaScript Iteration Protocols - Workshop NodeConf EU 2022
JavaScript Iteration Protocols - Workshop NodeConf EU 2022
Luciano Mammino
 
Building an invite-only microsite with Next.js & Airtable
Building an invite-only microsite with Next.js & AirtableBuilding an invite-only microsite with Next.js & Airtable
Building an invite-only microsite with Next.js & Airtable
Luciano Mammino
 
Let's take the monolith to the cloud 🚀
Let's take the monolith to the cloud 🚀Let's take the monolith to the cloud 🚀
Let's take the monolith to the cloud 🚀
Luciano Mammino
 
A look inside the European Covid Green Certificate - Rust Dublin
A look inside the European Covid Green Certificate - Rust DublinA look inside the European Covid Green Certificate - Rust Dublin
A look inside the European Covid Green Certificate - Rust Dublin
Luciano Mammino
 
Monoliths to the cloud!
Monoliths to the cloud!Monoliths to the cloud!
Monoliths to the cloud!
Luciano Mammino
 
The senior dev
The senior devThe senior dev
The senior dev
Luciano Mammino
 
Node.js: scalability tips - Azure Dev Community Vijayawada
Node.js: scalability tips - Azure Dev Community VijayawadaNode.js: scalability tips - Azure Dev Community Vijayawada
Node.js: scalability tips - Azure Dev Community Vijayawada
Luciano Mammino
 
A look inside the European Covid Green Certificate (Codemotion 2021)
A look inside the European Covid Green Certificate (Codemotion 2021)A look inside the European Covid Green Certificate (Codemotion 2021)
A look inside the European Covid Green Certificate (Codemotion 2021)
Luciano Mammino
 
AWS Observability Made Simple
AWS Observability Made SimpleAWS Observability Made Simple
AWS Observability Made Simple
Luciano Mammino
 
Semplificare l'observability per progetti Serverless
Semplificare l'observability per progetti ServerlessSemplificare l'observability per progetti Serverless
Semplificare l'observability per progetti Serverless
Luciano Mammino
 
Finding a lost song with Node.js and async iterators - NodeConf Remote 2021
Finding a lost song with Node.js and async iterators - NodeConf Remote 2021Finding a lost song with Node.js and async iterators - NodeConf Remote 2021
Finding a lost song with Node.js and async iterators - NodeConf Remote 2021
Luciano Mammino
 
Finding a lost song with Node.js and async iterators - EnterJS 2021
Finding a lost song with Node.js and async iterators - EnterJS 2021Finding a lost song with Node.js and async iterators - EnterJS 2021
Finding a lost song with Node.js and async iterators - EnterJS 2021
Luciano Mammino
 
How to send gzipped requests with boto3
How to send gzipped requests with boto3How to send gzipped requests with boto3
How to send gzipped requests with boto3
Luciano Mammino
 
Finding a lost song with Node.js and async iterators
Finding a lost song with Node.js and async iteratorsFinding a lost song with Node.js and async iterators
Finding a lost song with Node.js and async iterators
Luciano Mammino
 
AWS Observability (without the Pain)
AWS Observability (without the Pain)AWS Observability (without the Pain)
AWS Observability (without the Pain)
Luciano Mammino
 

Más de Luciano Mammino (20)

Did you know JavaScript has iterators? DublinJS
Did you know JavaScript has iterators? DublinJSDid you know JavaScript has iterators? DublinJS
Did you know JavaScript has iterators? DublinJS
 
What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...
What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...
What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...
 
Building an invite-only microsite with Next.js & Airtable - ReactJS Milano
Building an invite-only microsite with Next.js & Airtable - ReactJS MilanoBuilding an invite-only microsite with Next.js & Airtable - ReactJS Milano
Building an invite-only microsite with Next.js & Airtable - ReactJS Milano
 
From Node.js to Design Patterns - BuildPiper
From Node.js to Design Patterns - BuildPiperFrom Node.js to Design Patterns - BuildPiper
From Node.js to Design Patterns - BuildPiper
 
Let's build a 0-cost invite-only website with Next.js and Airtable!
Let's build a 0-cost invite-only website with Next.js and Airtable!Let's build a 0-cost invite-only website with Next.js and Airtable!
Let's build a 0-cost invite-only website with Next.js and Airtable!
 
JavaScript Iteration Protocols - Workshop NodeConf EU 2022
JavaScript Iteration Protocols - Workshop NodeConf EU 2022JavaScript Iteration Protocols - Workshop NodeConf EU 2022
JavaScript Iteration Protocols - Workshop NodeConf EU 2022
 
Building an invite-only microsite with Next.js & Airtable
Building an invite-only microsite with Next.js & AirtableBuilding an invite-only microsite with Next.js & Airtable
Building an invite-only microsite with Next.js & Airtable
 
Let's take the monolith to the cloud 🚀
Let's take the monolith to the cloud 🚀Let's take the monolith to the cloud 🚀
Let's take the monolith to the cloud 🚀
 
A look inside the European Covid Green Certificate - Rust Dublin
A look inside the European Covid Green Certificate - Rust DublinA look inside the European Covid Green Certificate - Rust Dublin
A look inside the European Covid Green Certificate - Rust Dublin
 
Monoliths to the cloud!
Monoliths to the cloud!Monoliths to the cloud!
Monoliths to the cloud!
 
The senior dev
The senior devThe senior dev
The senior dev
 
Node.js: scalability tips - Azure Dev Community Vijayawada
Node.js: scalability tips - Azure Dev Community VijayawadaNode.js: scalability tips - Azure Dev Community Vijayawada
Node.js: scalability tips - Azure Dev Community Vijayawada
 
A look inside the European Covid Green Certificate (Codemotion 2021)
A look inside the European Covid Green Certificate (Codemotion 2021)A look inside the European Covid Green Certificate (Codemotion 2021)
A look inside the European Covid Green Certificate (Codemotion 2021)
 
AWS Observability Made Simple
AWS Observability Made SimpleAWS Observability Made Simple
AWS Observability Made Simple
 
Semplificare l'observability per progetti Serverless
Semplificare l'observability per progetti ServerlessSemplificare l'observability per progetti Serverless
Semplificare l'observability per progetti Serverless
 
Finding a lost song with Node.js and async iterators - NodeConf Remote 2021
Finding a lost song with Node.js and async iterators - NodeConf Remote 2021Finding a lost song with Node.js and async iterators - NodeConf Remote 2021
Finding a lost song with Node.js and async iterators - NodeConf Remote 2021
 
Finding a lost song with Node.js and async iterators - EnterJS 2021
Finding a lost song with Node.js and async iterators - EnterJS 2021Finding a lost song with Node.js and async iterators - EnterJS 2021
Finding a lost song with Node.js and async iterators - EnterJS 2021
 
How to send gzipped requests with boto3
How to send gzipped requests with boto3How to send gzipped requests with boto3
How to send gzipped requests with boto3
 
Finding a lost song with Node.js and async iterators
Finding a lost song with Node.js and async iteratorsFinding a lost song with Node.js and async iterators
Finding a lost song with Node.js and async iterators
 
AWS Observability (without the Pain)
AWS Observability (without the Pain)AWS Observability (without the Pain)
AWS Observability (without the Pain)
 

Último

Cosa hanno in comune un mattoncino Lego e la backdoor XZ?
Cosa hanno in comune un mattoncino Lego e la backdoor XZ?Cosa hanno in comune un mattoncino Lego e la backdoor XZ?
Cosa hanno in comune un mattoncino Lego e la backdoor XZ?
Speck&Tech
 
Monitoring and Managing Anomaly Detection on OpenShift.pdf
Monitoring and Managing Anomaly Detection on OpenShift.pdfMonitoring and Managing Anomaly Detection on OpenShift.pdf
Monitoring and Managing Anomaly Detection on OpenShift.pdf
Tosin Akinosho
 
UI5 Controls simplified - UI5con2024 presentation
UI5 Controls simplified - UI5con2024 presentationUI5 Controls simplified - UI5con2024 presentation
UI5 Controls simplified - UI5con2024 presentation
Wouter Lemaire
 
Programming Foundation Models with DSPy - Meetup Slides
Programming Foundation Models with DSPy - Meetup SlidesProgramming Foundation Models with DSPy - Meetup Slides
Programming Foundation Models with DSPy - Meetup Slides
Zilliz
 
TrustArc Webinar - 2024 Global Privacy Survey
TrustArc Webinar - 2024 Global Privacy SurveyTrustArc Webinar - 2024 Global Privacy Survey
TrustArc Webinar - 2024 Global Privacy Survey
TrustArc
 
Project Management Semester Long Project - Acuity
Project Management Semester Long Project - AcuityProject Management Semester Long Project - Acuity
Project Management Semester Long Project - Acuity
jpupo2018
 
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdfUnlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
Malak Abu Hammad
 
5th LF Energy Power Grid Model Meet-up Slides
5th LF Energy Power Grid Model Meet-up Slides5th LF Energy Power Grid Model Meet-up Slides
5th LF Energy Power Grid Model Meet-up Slides
DanBrown980551
 
Columbus Data & Analytics Wednesdays - June 2024
Columbus Data & Analytics Wednesdays - June 2024Columbus Data & Analytics Wednesdays - June 2024
Columbus Data & Analytics Wednesdays - June 2024
Jason Packer
 
Fueling AI with Great Data with Airbyte Webinar
Fueling AI with Great Data with Airbyte WebinarFueling AI with Great Data with Airbyte Webinar
Fueling AI with Great Data with Airbyte Webinar
Zilliz
 
Webinar: Designing a schema for a Data Warehouse
Webinar: Designing a schema for a Data WarehouseWebinar: Designing a schema for a Data Warehouse
Webinar: Designing a schema for a Data Warehouse
Federico Razzoli
 
“Building and Scaling AI Applications with the Nx AI Manager,” a Presentation...
“Building and Scaling AI Applications with the Nx AI Manager,” a Presentation...“Building and Scaling AI Applications with the Nx AI Manager,” a Presentation...
“Building and Scaling AI Applications with the Nx AI Manager,” a Presentation...
Edge AI and Vision Alliance
 
Serial Arm Control in Real Time Presentation
Serial Arm Control in Real Time PresentationSerial Arm Control in Real Time Presentation
Serial Arm Control in Real Time Presentation
tolgahangng
 
HCL Notes and Domino License Cost Reduction in the World of DLAU
HCL Notes and Domino License Cost Reduction in the World of DLAUHCL Notes and Domino License Cost Reduction in the World of DLAU
HCL Notes and Domino License Cost Reduction in the World of DLAU
panagenda
 
HCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAU
HCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAUHCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAU
HCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAU
panagenda
 
How to use Firebase Data Connect For Flutter
How to use Firebase Data Connect For FlutterHow to use Firebase Data Connect For Flutter
How to use Firebase Data Connect For Flutter
Daiki Mogmet Ito
 
Energy Efficient Video Encoding for Cloud and Edge Computing Instances
Energy Efficient Video Encoding for Cloud and Edge Computing InstancesEnergy Efficient Video Encoding for Cloud and Edge Computing Instances
Energy Efficient Video Encoding for Cloud and Edge Computing Instances
Alpen-Adria-Universität
 
Artificial Intelligence for XMLDevelopment
Artificial Intelligence for XMLDevelopmentArtificial Intelligence for XMLDevelopment
Artificial Intelligence for XMLDevelopment
Octavian Nadolu
 
GraphRAG for Life Science to increase LLM accuracy
GraphRAG for Life Science to increase LLM accuracyGraphRAG for Life Science to increase LLM accuracy
GraphRAG for Life Science to increase LLM accuracy
Tomaz Bratanic
 
Digital Marketing Trends in 2024 | Guide for Staying Ahead
Digital Marketing Trends in 2024 | Guide for Staying AheadDigital Marketing Trends in 2024 | Guide for Staying Ahead
Digital Marketing Trends in 2024 | Guide for Staying Ahead
Wask
 

Último (20)

Cosa hanno in comune un mattoncino Lego e la backdoor XZ?
Cosa hanno in comune un mattoncino Lego e la backdoor XZ?Cosa hanno in comune un mattoncino Lego e la backdoor XZ?
Cosa hanno in comune un mattoncino Lego e la backdoor XZ?
 
Monitoring and Managing Anomaly Detection on OpenShift.pdf
Monitoring and Managing Anomaly Detection on OpenShift.pdfMonitoring and Managing Anomaly Detection on OpenShift.pdf
Monitoring and Managing Anomaly Detection on OpenShift.pdf
 
UI5 Controls simplified - UI5con2024 presentation
UI5 Controls simplified - UI5con2024 presentationUI5 Controls simplified - UI5con2024 presentation
UI5 Controls simplified - UI5con2024 presentation
 
Programming Foundation Models with DSPy - Meetup Slides
Programming Foundation Models with DSPy - Meetup SlidesProgramming Foundation Models with DSPy - Meetup Slides
Programming Foundation Models with DSPy - Meetup Slides
 
TrustArc Webinar - 2024 Global Privacy Survey
TrustArc Webinar - 2024 Global Privacy SurveyTrustArc Webinar - 2024 Global Privacy Survey
TrustArc Webinar - 2024 Global Privacy Survey
 
Project Management Semester Long Project - Acuity
Project Management Semester Long Project - AcuityProject Management Semester Long Project - Acuity
Project Management Semester Long Project - Acuity
 
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdfUnlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
 
5th LF Energy Power Grid Model Meet-up Slides
5th LF Energy Power Grid Model Meet-up Slides5th LF Energy Power Grid Model Meet-up Slides
5th LF Energy Power Grid Model Meet-up Slides
 
Columbus Data & Analytics Wednesdays - June 2024
Columbus Data & Analytics Wednesdays - June 2024Columbus Data & Analytics Wednesdays - June 2024
Columbus Data & Analytics Wednesdays - June 2024
 
Fueling AI with Great Data with Airbyte Webinar
Fueling AI with Great Data with Airbyte WebinarFueling AI with Great Data with Airbyte Webinar
Fueling AI with Great Data with Airbyte Webinar
 
Webinar: Designing a schema for a Data Warehouse
Webinar: Designing a schema for a Data WarehouseWebinar: Designing a schema for a Data Warehouse
Webinar: Designing a schema for a Data Warehouse
 
“Building and Scaling AI Applications with the Nx AI Manager,” a Presentation...
“Building and Scaling AI Applications with the Nx AI Manager,” a Presentation...“Building and Scaling AI Applications with the Nx AI Manager,” a Presentation...
“Building and Scaling AI Applications with the Nx AI Manager,” a Presentation...
 
Serial Arm Control in Real Time Presentation
Serial Arm Control in Real Time PresentationSerial Arm Control in Real Time Presentation
Serial Arm Control in Real Time Presentation
 
HCL Notes and Domino License Cost Reduction in the World of DLAU
HCL Notes and Domino License Cost Reduction in the World of DLAUHCL Notes and Domino License Cost Reduction in the World of DLAU
HCL Notes and Domino License Cost Reduction in the World of DLAU
 
HCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAU
HCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAUHCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAU
HCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAU
 
How to use Firebase Data Connect For Flutter
How to use Firebase Data Connect For FlutterHow to use Firebase Data Connect For Flutter
How to use Firebase Data Connect For Flutter
 
Energy Efficient Video Encoding for Cloud and Edge Computing Instances
Energy Efficient Video Encoding for Cloud and Edge Computing InstancesEnergy Efficient Video Encoding for Cloud and Edge Computing Instances
Energy Efficient Video Encoding for Cloud and Edge Computing Instances
 
Artificial Intelligence for XMLDevelopment
Artificial Intelligence for XMLDevelopmentArtificial Intelligence for XMLDevelopment
Artificial Intelligence for XMLDevelopment
 
GraphRAG for Life Science to increase LLM accuracy
GraphRAG for Life Science to increase LLM accuracyGraphRAG for Life Science to increase LLM accuracy
GraphRAG for Life Science to increase LLM accuracy
 
Digital Marketing Trends in 2024 | Guide for Staying Ahead
Digital Marketing Trends in 2024 | Guide for Staying AheadDigital Marketing Trends in 2024 | Guide for Staying Ahead
Digital Marketing Trends in 2024 | Guide for Staying Ahead
 

Everything I know about S3 pre-signed URLs

  • 1. AWS User Group: Dublin Meetup / 2022-11-17 1
  • 3. 😎"I have a startup idea..." loige fth.link/presign 3
  • 9. Yes, I still have to implement the profile picture upload feature... 😅 loige 9
  • 10. $ ~ whoami 👋I'm Luciano ( 🍕🍝 ) Senior Architect @ fourTheorem (Dublin ) nodejsdp.link 📔Co-Author of Node.js Design Patterns 👉 Let's connect! (blog) (twitter) (twitch) (github) loige.co @loige loige lmammino 10
  • 11. Always re-imagining We are a pioneering technology consultancy focused on AWS and serverless | | Accelerated Serverless AI as a Service Platform Modernisation loige ✉Reach out to us at 😇We are always looking for talent: hello@fourTheorem.com fth.link/careers 11
  • 12. We host a weekly podcast about AWS awsbites.com loige 12
  • 13. 🤔 "upload" feature in a web app... loige 13
  • 14. What's an upload, really? loige 14
  • 16. loige Structure of an HTTP request POST /profilepic/upload HTTP/1.1 Host: api.meower.com Content-Type: text/plain Content-Length: 9 Some data Method Path Version Headers Body 16
  • 17. loige What if it's a binary (like a picture)? PUT /profilepic/upload HTTP/1.1 Host: api.meower.com Content-Type: image/jpeg Content-Length: 2097852 ����JFIFHH������"�� ���Dl��FW�'6N�()H�'p��FD3 [...] read 2097852 bytes 17
  • 18. loige Using Lambda Lambda proxy integration* * JSON Based protocol mapping to HTTP (JSON Request / JSON Response) 18
  • 19. { "resource": "/profilepic/upload", "path": "/profilepic/upload", "httpMethod": "POST", "headers": { "Content-Type": "text/plain", "Content-Length": "9", "Host": "api.meower.com", }, "body": "Some data" } 1 2 3 4 5 6 7 8 9 10 11 "resource": "/profilepic/upload", "path": "/profilepic/upload", { 1 2 3 "httpMethod": "POST", 4 "headers": { 5 "Content-Type": "text/plain", 6 "Content-Length": "9", 7 "Host": "api.meower.com", 8 }, 9 "body": "Some data" 10 } 11 "httpMethod": "POST", { 1 "resource": "/profilepic/upload", 2 "path": "/profilepic/upload", 3 4 "headers": { 5 "Content-Type": "text/plain", 6 "Content-Length": "9", 7 "Host": "api.meower.com", 8 }, 9 "body": "Some data" 10 } 11 "headers": { "Content-Type": "text/plain", "Content-Length": "9", "Host": "api.meower.com", }, { 1 "resource": "/profilepic/upload", 2 "path": "/profilepic/upload", 3 "httpMethod": "POST", 4 5 6 7 8 9 "body": "Some data" 10 } 11 "body": "Some data" { 1 "resource": "/profilepic/upload", 2 "path": "/profilepic/upload", 3 "httpMethod": "POST", 4 "headers": { 5 "Content-Type": "text/plain", 6 "Content-Length": "9", 7 "Host": "api.meower.com", 8 }, 9 10 } 11 { "resource": "/profilepic/upload", "path": "/profilepic/upload", "httpMethod": "POST", "headers": { "Content-Type": "text/plain", "Content-Length": "9", "Host": "api.meower.com", }, "body": "Some data" } 1 2 3 4 5 6 7 8 9 10 11 loige Lambda proxy integration (request) 19
  • 20. { "resource": "/profilepic/upload", "path": "/profilepic/upload", "httpMethod": "PUT", "headers": { "Content-Type": "image/jpeg", "Content-Length": "2097852", "Host": "api.meower.com", }, "body": "????????" } 1 2 3 4 5 6 7 8 9 10 11 "Content-Type": "image/jpeg", "Content-Length": "2097852", { 1 "resource": "/profilepic/upload", 2 "path": "/profilepic/upload", 3 "httpMethod": "PUT", 4 "headers": { 5 6 7 "Host": "api.meower.com", 8 }, 9 "body": "????????" 10 } 11 "body": "????????" { 1 "resource": "/profilepic/upload", 2 "path": "/profilepic/upload", 3 "httpMethod": "PUT", 4 "headers": { 5 "Content-Type": "image/jpeg", 6 "Content-Length": "2097852", 7 "Host": "api.meower.com", 8 }, 9 10 } 11 { "resource": "/profilepic/upload", "path": "/profilepic/upload", "httpMethod": "PUT", "headers": { "Content-Type": "image/jpeg", "Content-Length": "2097852", "Host": "api.meower.com", }, "body": "????????" } 1 2 3 4 5 6 7 8 9 10 11 loige Lambda proxy integration (request - picture) 20
  • 21. "isBase64Encoded": true, "body": "/9j/4AAQSkZJRgABAQEASABIAAD/2w[...]" { 1 "resource": "/profilepic/upload", 2 "path": "/profilepic/upload", 3 "httpMethod": "PUT", 4 "headers": { 5 "Content-Type": "image/jpeg", 6 "Content-Length": "2097852", 7 "Host": "api.meower.com", 8 }, 9 10 11 } 12 { "resource": "/profilepic/upload", "path": "/profilepic/upload", "httpMethod": "PUT", "headers": { "Content-Type": "image/jpeg", "Content-Length": "2097852", "Host": "api.meower.com", }, "isBase64Encoded": true, "body": "/9j/4AAQSkZJRgABAQEASABIAAD/2w[...]" } 1 2 3 4 5 6 7 8 9 10 11 12 loige Lambda proxy integration (request - picture) 21
  • 22. loige 1. Parse request (JSON) 2. Decode body (Base64) 3. Validation / resize 4. ... Lambda proxy integration request /profilepic/upload 22
  • 23. loige 1. Parse request (JSON) 2. Decode body (Base64) 3. Validation / resize 4. ... Lambda proxy integration request /profilepic/upload 😎 23
  • 24. loige Is this a good solution? 🙄 24
  • 25. loige Limitations... API Gateway requests timeout: 30 sec API Gateway payload: max 10 MB Lambda timeout: max 15 mins Lambda payload size: max 6 MB Upload: 6 MB / 30 sec 25
  • 26. loige Is this a good solution? 🙄 ... not really! What about supporting big images or even videos? 26
  • 27. loige An alternative approach ✅ Long lived connection ✅ No size limit 27
  • 29. loige S3 pre-signed URLs 😱 An S3 built-in feature to authorize operations (download, upload, etc) on a bucket / object using time-limited authenticated URLs 29
  • 30. loige Using S3 pre-signed URLs for upload * yeah, this can be a Lambda as well 😇 * 30
  • 31. loige Using S3 pre-signed URLs for upload * yeah, this can be a Lambda as well 😇 * 31
  • 32. loige Using S3 pre-signed URLs for upload * yeah, this can be a Lambda as well 😇 * 32
  • 33. loige Using S3 pre-signed URLs for upload * yeah, this can be a Lambda as well 😇 * 33
  • 34. loige Using S3 pre-signed URLs for upload ✅ * yeah, this can be a Lambda as well 😇 * 34
  • 35. loige ... and we can also use it for downloads! 🤩 35
  • 36. loige Using S3 pre-signed URLs for download 36
  • 37. loige Using S3 pre-signed URLs for download 37
  • 38. loige Using S3 pre-signed URLs for download 38
  • 39. loige Using S3 pre-signed URLs for download 39
  • 40. loige Using S3 pre-signed URLs for download ✅ 40
  • 41. loige ⚠VERY important details! I lied to you a little in those diagrams... 🤥 It's a decent mental model, but it's not accurate 😅 The server never really talks with S3! The server actually creates the signed URL by itself! We will see later what's the security model around this idea! 41
  • 42. loige Is this a good solution? 🙄 ✅It's a managed feature (a.k.a. no servers to manage) ✅We can upload and download arbitrarily big files with no practical limits* ✅Reasonably simple and secure 👍Seems good to me! * objects in S3 are "limited" to 5TB (when using multi-part upload), 5 GB otherwise. 42
  • 43. loige Generating our first pre-signed URL $ aws s3 presign s3://finance-department-bucket/2022/tax-certificate.pdf https://s3.amazonaws.com/finance-department-bucket/2022/tax- certificate.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz- Credential=AKIA3SGQVQG7FGA6KKA6%2F20221104%2Fus-east- 1%2Fs3%2Faws4_request&X-Amz-Date=20221104T140227Z&X-Amz- Expires=3600&X-Amz-SignedHeaders=host&X-Amz- Signature=b228dbec8c1008c80c162e1210e4503dceead1e4d4751b4d9787 314fd6da4d55 Whoever has this URL can download the tax certificate! 43
  • 44. loige What's in a pre-signed URL https://s3.amazonaws.com/finance-department-bucket/2022/tax- certificate.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz- Credential=AKIA3SGQVQG7FGA6KKA6%2F20221104%2Fus-east- 1%2Fs3%2Faws4_request&X-Amz-Date=20221104T140227Z&X-Amz- Expires=3600&X-Amz-SignedHeaders=host&X-Amz- Signature=b228dbec8c1008c80c162e1210e4503dceead1e4d4751b4d9787 314fd6da4d55 44
  • 45. loige What's in a pre-signed URL https://s3.amazonaws.com /finance-department-bucket /2022/tax-certificate.pdf ?X-Amz-Algorithm=AWS4-HMAC-SHA256 &X-Amz-Credential=AKIA3SGQXQG7XXXYKKA6%2F20221104... &X-Amz-Date=20221104T140227Z &X-Amz-Expires=3600 &X-Amz-SignedHeaders=host &X-Amz-Signature=b228dbec8c1008c80c162e1210e4503dceead1e4d4... What if I change this to /passwords.txt? 45
  • 48. loige 🤓 Once a pre-signed URL is generated you cannot edit it without breaking it Photo by on CHUTTERSNAP Unsplash ⚠Also note that you can use a pre-signed URL as many times as you want until it expires 48
  • 49. loige 🔐Permissions Anyone with valid credentials can create a pre-signed URL (client side) valid credentials = Role, User, or Security Token The generated URL inherits the permissions of the credentials used to generate it This means you can generate pre-signed URLs for things you don't have access to 😅 49
  • 50. loige $ aws s3 presign s3://ireland/i-love-you https://ireland.s3.eu-west-1.amazonaws.com/i-love-you?X-Amz- Algorithm=AWS4-HMAC-SHA256&X-Amz- Credential=AKIA3ABCVQG7FGA6KKA6%2F20221115%2Feu-west- 1%2Fs3%2Faws4_request&X-Amz-Date=20221115T182036Z&X-Amz- Expires=3600&X-Amz-SignedHeaders=host&X-Amz- Signature=75749c92d94d03e411e7bbf64419f2af09301d1791b0df54c639 137c715f7888 😱 I swear I don't even know if this bucket exists or who owns it! 50
  • 51. loige Pre-signed URLs are validated at request time 51
  • 52. loige Creating a pre-signed URL programmatically AWS SDK for JavaScript v3 52
  • 53. import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3' import { getSignedUrl } from '@aws-sdk/s3-request-presigner' const s3Client = new S3Client() const command = new GetObjectCommand({ Bucket: "some-bucket", Key: "some-object" }) const preSignedUrl = await getSignedUrl(s3Client, command, { expiresIn: 3600 }) console.log(preSignedUrl) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3' import { getSignedUrl } from '@aws-sdk/s3-request-presigner' 1 2 3 const s3Client = new S3Client() 4 5 const command = new GetObjectCommand({ 6 Bucket: "some-bucket", 7 Key: "some-object" 8 }) 9 10 const preSignedUrl = await getSignedUrl(s3Client, command, { 11 expiresIn: 3600 12 }) 13 14 console.log(preSignedUrl) 15 const s3Client = new S3Client() import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3' 1 import { getSignedUrl } from '@aws-sdk/s3-request-presigner' 2 3 4 5 const command = new GetObjectCommand({ 6 Bucket: "some-bucket", 7 Key: "some-object" 8 }) 9 10 const preSignedUrl = await getSignedUrl(s3Client, command, { 11 expiresIn: 3600 12 }) 13 14 console.log(preSignedUrl) 15 const command = new GetObjectCommand({ Bucket: "some-bucket", Key: "some-object" }) import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3' 1 import { getSignedUrl } from '@aws-sdk/s3-request-presigner' 2 3 const s3Client = new S3Client() 4 5 6 7 8 9 10 const preSignedUrl = await getSignedUrl(s3Client, command, { 11 expiresIn: 3600 12 }) 13 14 console.log(preSignedUrl) 15 const preSignedUrl = await getSignedUrl(s3Client, command, { expiresIn: 3600 }) import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3' 1 import { getSignedUrl } from '@aws-sdk/s3-request-presigner' 2 3 const s3Client = new S3Client() 4 5 const command = new GetObjectCommand({ 6 Bucket: "some-bucket", 7 Key: "some-object" 8 }) 9 10 11 12 13 14 console.log(preSignedUrl) 15 console.log(preSignedUrl) import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3' 1 import { getSignedUrl } from '@aws-sdk/s3-request-presigner' 2 3 const s3Client = new S3Client() 4 5 const command = new GetObjectCommand({ 6 Bucket: "some-bucket", 7 Key: "some-object" 8 }) 9 10 const preSignedUrl = await getSignedUrl(s3Client, command, { 11 expiresIn: 3600 12 }) 13 14 15 import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3' import { getSignedUrl } from '@aws-sdk/s3-request-presigner' const s3Client = new S3Client() const command = new GetObjectCommand({ Bucket: "some-bucket", Key: "some-object" }) const preSignedUrl = await getSignedUrl(s3Client, command, { expiresIn: 3600 }) console.log(preSignedUrl) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 loige 53
  • 55. loige 2 Options: PUT & POST 🤨 55
  • 56. loige PUT Method PUT <preSignedURL> HTTP/1.1 Host: <bucket>.s3.<region>.amazonaws.com Content-Length: 2097852 ����JFIFHH������"�� ���Dl��FW�'6N�()H�'p��FD3 [...] 56
  • 57. import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3' import { getSignedUrl } from '@aws-sdk/s3-request-presigner' const s3Client = new S3Client() const command = new PutObjectCommand({ Bucket: "some-bucket", Key: "some-object" }) const preSignedUrl = await getSignedUrl(s3Client, command, { expiresIn: 3600 }) console.log(preSignedUrl) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 loige Only difference with the previous example 57
  • 58. loige PUT Method - Limitations You cannot set a limit on the upload size (max of 5 GB)! * You can limit the Content-Type but you can specify exactly one * Unless you know the exact size in advance 58
  • 59. loige POST method It uses the multipart/form-data encoding (form upload) Gives more freedom to the client to shape the request (Content-Type, file name, etc) It uses a policy mechanism to define the "rules" of what can be uploaded E.g. you can limit the supported mime types and provide a maximum file size You can use it to upload from a web form and even configure the redirect URL It's not really a URL but more of a pre-signed form! 59
  • 60. POST / HTTP/1.1 Host: <bucket>.s3.amazonaws.com Content-Type: multipart/form-data; boundary=9431149156168 Content-Length: 2097852 --9431149156168 Content-Disposition: form-data; name="key" picture.jpg --9431149156168 Content-Disposition: form-data; name="X-Amz-Credential" AKIA3SGABCDXXXA6KKA6/20221115/eu-west-1/s3/aws4_request --9431149156168 Content-Disposition: form-data; name="Policy" eyJleHBpcmF0aW9uIjoiMjAyMi0xMS0xNVQyMDo0NjozN1oiLCJjb25kaXRpb25zIjpbWyJj[...] --9431149156168 Content-Disposition: form-data; name="X-Amz-Signature" 2c1da0001dfec7caea1c9fb80c7bc8847f515a9e4483d2942464f48d2f827de7 --9431149156168 Content-Disposition: form-data; name="file"; filename="MyFilename.jpg" Content-Type: image/jpeg ����JFIFHH������"�� ���Dl��FW�'6N�()H�'p��FD3[...] --9431149156168-- loige 60
  • 61. loige POST method Policy A JSON object (Base64 encoded) that defines the upload rules (conditions) and the expiration date This is what gets signed: you cannot alter the policy without breaking the signature { "expiration": "2022-11-15T20:46:37Z", "conditions": [ ["content-length-range", 0, 5242880], ["starts-with", "$Content-Type", "image/"], {"bucket": "somebucket"}, {"X-Amz-Algorithm": "AWS4-HMAC-SHA256"}, {"X-Amz-Credential": "AKIA3SGABCDXXXA6KKA6/20221115/eu-west-1/s3/aws4_request"}, {"X-Amz-Date": "20221115T194637Z"}, {"key": "picture.jpg"} ] } 61
  • 62. import { S3Client } from '@aws-sdk/client-s3' import { createPresignedPost } from '@aws-sdk/s3-presigned-post' const { BUCKET_NAME, OBJECT_KEY } = process.env const s3Client = new S3Client() const { url, fields } = await createPresignedPost(s3Client, { Bucket: 'somebucket', Key: 'someobject', Conditions: [ ['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max ], Fields: { success_action_status: '201', 'Content-Type': 'image/png' }, Expires: 3600 }) console.log({ url, fields }) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import { createPresignedPost } from '@aws-sdk/s3-presigned-post' import { S3Client } from '@aws-sdk/client-s3' 1 2 3 const { BUCKET_NAME, OBJECT_KEY } = process.env 4 const s3Client = new S3Client() 5 6 const { url, fields } = await createPresignedPost(s3Client, { 7 Bucket: 'somebucket', 8 Key: 'someobject', 9 Conditions: [ 10 ['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max 11 ], 12 Fields: { 13 success_action_status: '201', 14 'Content-Type': 'image/png' 15 }, 16 Expires: 3600 17 }) 18 19 console.log({ url, fields }) 20 const { url, fields } = await createPresignedPost(s3Client, { }) import { S3Client } from '@aws-sdk/client-s3' 1 import { createPresignedPost } from '@aws-sdk/s3-presigned-post' 2 3 const { BUCKET_NAME, OBJECT_KEY } = process.env 4 const s3Client = new S3Client() 5 6 7 Bucket: 'somebucket', 8 Key: 'someobject', 9 Conditions: [ 10 ['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max 11 ], 12 Fields: { 13 success_action_status: '201', 14 'Content-Type': 'image/png' 15 }, 16 Expires: 3600 17 18 19 console.log({ url, fields }) 20 Bucket: 'somebucket', import { S3Client } from '@aws-sdk/client-s3' 1 import { createPresignedPost } from '@aws-sdk/s3-presigned-post' 2 3 const { BUCKET_NAME, OBJECT_KEY } = process.env 4 const s3Client = new S3Client() 5 6 const { url, fields } = await createPresignedPost(s3Client, { 7 8 Key: 'someobject', 9 Conditions: [ 10 ['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max 11 ], 12 Fields: { 13 success_action_status: '201', 14 'Content-Type': 'image/png' 15 }, 16 Expires: 3600 17 }) 18 19 console.log({ url, fields }) 20 Key: 'someobject', import { S3Client } from '@aws-sdk/client-s3' 1 import { createPresignedPost } from '@aws-sdk/s3-presigned-post' 2 3 const { BUCKET_NAME, OBJECT_KEY } = process.env 4 const s3Client = new S3Client() 5 6 const { url, fields } = await createPresignedPost(s3Client, { 7 Bucket: 'somebucket', 8 9 Conditions: [ 10 ['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max 11 ], 12 Fields: { 13 success_action_status: '201', 14 'Content-Type': 'image/png' 15 }, 16 Expires: 3600 17 }) 18 19 console.log({ url, fields }) 20 Conditions: [ ['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max ], import { S3Client } from '@aws-sdk/client-s3' 1 import { createPresignedPost } from '@aws-sdk/s3-presigned-post' 2 3 const { BUCKET_NAME, OBJECT_KEY } = process.env 4 const s3Client = new S3Client() 5 6 const { url, fields } = await createPresignedPost(s3Client, { 7 Bucket: 'somebucket', 8 Key: 'someobject', 9 10 11 12 Fields: { 13 success_action_status: '201', 14 'Content-Type': 'image/png' 15 }, 16 Expires: 3600 17 }) 18 19 console.log({ url, fields }) 20 Fields: { success_action_status: '201', 'Content-Type': 'image/png' }, import { S3Client } from '@aws-sdk/client-s3' 1 import { createPresignedPost } from '@aws-sdk/s3-presigned-post' 2 3 const { BUCKET_NAME, OBJECT_KEY } = process.env 4 const s3Client = new S3Client() 5 6 const { url, fields } = await createPresignedPost(s3Client, { 7 Bucket: 'somebucket', 8 Key: 'someobject', 9 Conditions: [ 10 ['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max 11 ], 12 13 14 15 16 Expires: 3600 17 }) 18 19 console.log({ url, fields }) 20 Expires: 3600 import { S3Client } from '@aws-sdk/client-s3' 1 import { createPresignedPost } from '@aws-sdk/s3-presigned-post' 2 3 const { BUCKET_NAME, OBJECT_KEY } = process.env 4 const s3Client = new S3Client() 5 6 const { url, fields } = await createPresignedPost(s3Client, { 7 Bucket: 'somebucket', 8 Key: 'someobject', 9 Conditions: [ 10 ['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max 11 ], 12 Fields: { 13 success_action_status: '201', 14 'Content-Type': 'image/png' 15 }, 16 17 }) 18 19 console.log({ url, fields }) 20 console.log({ url, fields }) import { S3Client } from '@aws-sdk/client-s3' 1 import { createPresignedPost } from '@aws-sdk/s3-presigned-post' 2 3 const { BUCKET_NAME, OBJECT_KEY } = process.env 4 const s3Client = new S3Client() 5 6 const { url, fields } = await createPresignedPost(s3Client, { 7 Bucket: 'somebucket', 8 Key: 'someobject', 9 Conditions: [ 10 ['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max 11 ], 12 Fields: { 13 success_action_status: '201', 14 'Content-Type': 'image/png' 15 }, 16 Expires: 3600 17 }) 18 19 20 import { S3Client } from '@aws-sdk/client-s3' import { createPresignedPost } from '@aws-sdk/s3-presigned-post' const { BUCKET_NAME, OBJECT_KEY } = process.env const s3Client = new S3Client() const { url, fields } = await createPresignedPost(s3Client, { Bucket: 'somebucket', Key: 'someobject', Conditions: [ ['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max ], Fields: { success_action_status: '201', 'Content-Type': 'image/png' }, Expires: 3600 }) console.log({ url, fields }) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 loige 62
  • 63. // you can use `url` and `fields` to generate an HTML form const code = `<h1>Upload an image to S3</h1> <form action="${url}" method="post" enctype="multipart/form-data"> ${Object.entries(fields).map(([key, value]) => { return `<input type="hidden" name="${key}" value="${value.replace(/"/g, '&quot;')}">` }).join('n')} <div><input type="file" name="file" accept="image/png"></div> <div><input type="submit" value="Upload"></div> </form>` 1 2 3 4 5 6 7 8 9 10 loige 63
  • 64. loige Limitations and quirks It supports only 1 file (cannot upload multiple files in one go) The file field must be the last entry in the form (S3 will ignore every other field after the file) From the browser (AJAX) you need to enable CORS on the bucket 64
  • 65. loige Should I use PUT or POST? 🧐 PUT is simpler but definitely more limited POST is slightly more complicated (and less adopted) but it's more flexible You should probably put some time into learning POST and use that! 65
  • 66. loige Pre-signed URLs for other operations S3 pre-signed URLs are not limited to GET, PUT or POST operations You can literally create pre-signed URLs for any command (DeleteObject, ListBuckets, MultiPartUpload, etc...) 66
  • 67. loige Do you need moar examples? 😼 github.com/lmammino/s3-presigned-urls-examples 67
  • 68. loige ... In summary S3 pre-signed URLs are a great way to authorise operations on S3 They are generally used to implement upload/download features The signature is created client-side so you can sign anything. Access is validated at request time This is not the only solution, you can also use the JavaScript SDK from the frontend and get limited credentials from Cognito (Amplify makes that process simpler) For upload you can use PUT and POST, but POST is much more flexible 💬PS: Meower.com doesn't really exist... but... do you want to invest?! It's a great idea, trust me! 68
  • 69. Cover photo by on Kelly Sikkema Unsplash fourtheorem.com THANKS! 🙌 fth.link/presign loige It's a wrap! 69