-1

I want access an image stored in s3 bucket using pre-signed url.This image needs to access who has the pre-signed url.

I have no idea what signature is it getting. If it's getting my signature, then how it's works for other users.

When I doing this. It will show this error:

<Error>
<Code>SignatureDoesNotMatch</Code>
    </Message>
    <AWSAccessKeyId>XX</AWSAccessKeyId>
    <StringToSign>GET 1900675866 /bucketname/161305.jpg</StringToSign>
    <SignatureProvided>xxxx</SignatureProvided>
    <StringToSignBytes>
    47 45 54 0a 0a 0a 31 39 30 30 36 37 35 38 36 36 0a 2f 6b 6c 70 2d 6d 65 64 69 61 2f 31 36 31 33 30 35 2e 6a 70 67
    </StringToSignBytes>
    <RequestId>xxxxx</RequestId>
    <HostId>
    xxxx
    </HostId>
    </Error>

Here is the code I tried to generate pre-signed url:

exports.handler = async function (event, context) {
    console.log('strarting to generate pre-signed image url');

     let s3BucketName = process.env.S3_BUCKET_NAME;
    const request = JSON.parse(event.body);
    const objectKey = request.key;
    const studentId = request.studentId;
    console.log(' generate pre-signed image url for:' + objectKey);

    console.log('Started getting pre signed url for:' + objectKey);

    let params = {
        Bucket: s3BucketName,
        Key: objectKey,
        Expires: 60 * 60 * 24 * 365 * 10,
        ContentType: 'image/jpg'
    };
    return await s3.getSignedUrlPromise('putObject', params).then(async url => {
        console.log('Successfully generated pre signed image url', url);
         const payload = {
            message: 'success',
            imageUrl: url
        };
        const response = {
            statusCode: 201,
            headers: responseHeaders,
            body: JSON.stringify(payload),
        };
        return response;
    }).catch(error => {
        console.error(`Failed to generate presigned url: ${error}`);
        return {
            statusCode: error.statusCode || 501,

            headers: responseHeaders,
            body: JSON.stringify({
                message: `Failed to generate presigned url. Request Id: ${context.awsRequestId}`
            })
        };
    });


}

In aws side, I blocked all public access. Here is the bucket policy:

{
    "Version": "2008-10-17",
    "Statement": [
        {
            "Sid": "AllowPublicRead",
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::media/*"
        }
    ]
}
Mahdi Zarei
  • 5,644
  • 7
  • 24
  • 54
DevAra
  • 531
  • 3
  • 10
  • 35

4 Answers4

1

You are trying to generate a presigned url for PutObject. Try "GetObject" instead

1

there is two methods using this you can use Pre Sign-in URL. same way i am using in my project.

  1. $path : where u store or upload file.

  2. $disk : name is disk u r using in laravel. like ( s3 )

    function  getS3PreSignURL($path ,$disk)
     {
       $s3 = \Storage::disk($disk);
       $client = $s3->getDriver()->getAdapter()->getClient();
       $expiry = "+5 minutes";
       $command = $client->getCommand('GetObject', [
      'Bucket' => Config::get('filesystems.disks.'.$disk.'.bucket'),
      //'Key'    => "uploads/1***1/image/030685.jpeg"
      'Key'    => $path
     ]);
     $request = $client->createPresignedRequest($command, $expiry);
     return (string) $request->getUri();
    } 
    
    
    function getS3StreamUrl($path , $disk)
    {
      $s3 = \Storage::disk($disk);
      $s3->getDriver()->getAdapter()->getClient()->registerStreamWrapper();
       return $fileUrl = "s3://".env("AWS_BUCKET").'/'.$path;
       if (is_file($fileUrl) && file_exists($fileUrl)) {
         return $fileUrl;
       } else {
          return false;
        }
      }
    

getS3StreamUrl will be used in FPDF pdf generation. because getS3PreSignURL return AWS token in link so that will not work in FPDF.

pankaj
  • 1
  • 17
  • 36
0

The whole point of presigned URL is to give temporary access to the objects in an s3 bucket. you dont need to create aws credentials and give access.

Presigned URL is generated using an AWS credentials (AccessKeyId, SecretAccessKeyId) of a user(in this case you) who has access to the specific s3 operation such as GetObject or PutObject. Once the url is generated, The url can be accessed by anyone over the internet. The presigned url is valid only for a certain time. If you haven't specified an expiry date, it will expire in 7 days.

When the presigned URL is accessed, AWS will calculate a signature in their end and then compare that singature value with signature passed in the URL.

In this casem As @vijilvpillai712 said, you are generating presigned url for putObject and trying to access via HTTP GetObject.

for content type, use content/jpeg not content/jpg. thats the correct mime type. But it has nothing to do with your issue.

Arun Kamalanathan
  • 8,107
  • 4
  • 23
  • 39
0

First you have to create a user on IAM inside aws site. Then git it a group with a policy that allow to acess S3 (S3 Full access), then inside the code you have to log with the credentials that he gives to you. Then use the following code from amazon to get the presigned_url that allows you to use in a variety of things:

def create_presigned_url(bucket_name, object_name, expiration=3600):
"""Generate a presigned URL to share an S3 object

:param bucket_name: string
:param object_name: string
:param expiration: Time in seconds for the presigned URL to remain valid
:return: Presigned URL as string. If error, returns None.
"""

# Generate a presigned URL for the S3 object
s3_client = boto3.client('s3')
try:
    response = s3_client.generate_presigned_url('get_object',
                                                Params={'Bucket': bucket_name,
                                                        'Key': object_name},
                                                ExpiresIn=expiration)
except ClientError as e:
    logging.error(e)
    return None

# The response contains the presigned URL
return response