Creating Pre-Signed URLs for Amazon S3 Buckets - AWS SDK for Go (version 1)

We announced the upcoming end-of-support for AWS SDK for Go V1. We recommend that you migrate to AWS SDK for Go V2. For dates, additional details, and information on how to migrate, please refer to the linked announcement.

Creating Pre-Signed URLs for Amazon S3 Buckets

This Go example shows you how to obtain a pre-signed URL for an Amazon S3 bucket. You can download complete versions of these example files from the aws-doc-sdk-examples repository on GitHub.

Scenario

In this example, a series of Go routines are used to obtain a pre-signed URL for an Amazon S3 bucket using either GetObject or a PUT operation. A pre-signed URL allows you to grant temporary access to users who don’t have permission to directly run AWS operations in your account. A pre-signed URL is signed with your credentials and can be used by any user.

Prerequisites

Generate a Pre-Signed URL for a GetObject Operation

To generate a pre-signed URL, use the Presign method on the request object. You must set an expiration value because the AWS SDK for Go doesn’t set one by default.

The following example generates a pre-signed URL that enables you to temporarily share a file without making it public. Anyone with access to the URL can view the file.

package main import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/s3" "log" "time" ) // Downloads an item from an S3 Bucket // // Usage: // go run s3_download.go func main() { // Initialize a session in us-west-2 that the SDK will use to load // credentials from the shared credentials file ~/.aws/credentials. sess, err := session.NewSession(&aws.Config{ Region: aws.String("us-west-2")}, ) // Create S3 service client svc := s3.New(sess) req, _ := svc.GetObjectRequest(&s3.GetObjectInput{ Bucket: aws.String("myBucket"), Key: aws.String("myKey"), }) urlStr, err := req.Presign(15 * time.Minute) if err != nil { log.Println("Failed to sign request", err) } log.Println("The URL is", urlStr) }

If the SDK has not retrieved your credentials before calling Presign, it will get them to generate the pre-signed URL.

Generate a Pre-Signed URL for an Amazon S3 PUT Operation with a Specific Payload

You can generate a pre-signed URL for a PUT operation that checks whether users upload the correct content. When the SDK pre-signs a request, it computes the checksum of the request body and generates an MD5 checksum that is included in the pre-signed URL. Users must upload the same content that produces the same MD5 checksum generated by the SDK; otherwise, the operation fails. This is not the Content-MD5, but the signature. To enforce Content-MD5, simply add the header to the request.

The following example adds a Body field to generate a pre-signed PUT operation that requires a specific payload to be uploaded by users.

package main import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/s3" "log" "strings" "time" ) func main() { // Initialize a session in us-west-2 that the SDK will use to load // credentials from the shared credentials file ~/.aws/credentials. sess, err := session.NewSession(&aws.Config{ Region: aws.String("us-west-2")}, ) // Create S3 service client svc := s3.New(sess) req, _ := svc.PutObjectRequest(&s3.PutObjectInput{ Bucket: aws.String("myBucket"), Key: aws.String("myKey"), Body: strings.NewReader("EXPECTED CONTENTS"), }) str, err := req.Presign(15 * time.Minute) log.Println("The URL is:", str, " err:", err) }

If you omit the Body field, users can write any contents to the given object.

The following example shows the enforcing of Content-MD5.

package main import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/s3" "encoding/base64" "fmt" "crypto/md5" "strings" "time" "net/http" ) // Downloads an item from an S3 Bucket in the region configured in the shared config // or AWS_REGION environment variable. // // Usage: // go run s3_download.go func main() { h := md5.New() content := strings.NewReader("") content.WriteTo(h) // Initialize a session in us-west-2 that the SDK will use to load // credentials from the shared credentials file ~/.aws/credentials. sess, err := session.NewSession(&aws.Config{ Region: aws.String("us-west-2")}, ) // Create S3 service client svc := s3.New(sess) resp, _ := svc.PutObjectRequest(&s3.PutObjectInput{ Bucket: aws.String("testBucket"), Key: aws.String("testKey"), }) md5s := base64.StdEncoding.EncodeToString(h.Sum(nil)) resp.HTTPRequest.Header.Set("Content-MD5", md5s) url, err := resp.Presign(15 * time.Minute) if err != nil { fmt.Println("error presigning request", err) return } req, err := http.NewRequest("PUT", url, strings.NewReader("")) req.Header.Set("Content-MD5", md5s) if err != nil { fmt.Println("error creating request", url) return } defClient, err := http.DefaultClient.Do(req) fmt.Println(defClient, err) }