4

I'm using this class to generate a direct upload form which includes the policy part.

https://designedbyaturtle.com/direct-upload-to-s3-using-aws-signature-v4-php/

The uploads are working but I want to be able to display the file from the url for users on the site without making the files public.

I understand the SDK has a simple method for this but I am hoping I can do it with the existing code as this already creates the policy. I'm wondering what are hte steps for creating this url from scratch? It seems excessive to include the entire bloated SDK for just one function.

2
  • 1
    I'd say you'd want to start at docs.aws.amazon.com/AmazonS3/latest/API/Welcome.html and locate the REST API on how to create a signed URL. Whilst your SDK size bloat might be a concern, its autoloading is fantastic and the memory impact should be low. Commented Oct 21, 2017 at 11:53
  • The policy that is signed for direct uploads is entirely different from the canonical request that is signed for pre-signed URLs. However, there is useful code in that example for constructing the signing key... so this is a helpful start. Commented Oct 21, 2017 at 15:55

3 Answers 3

2

I solved it by using these two classes and modifying them a bit to make a class with two functions geturl and getform instead of using the API, these work great for v4 signatures.

getform:

https://www.designedbyaturtle.co.uk/2015/direct-upload-to-s3-using-aws-signature-v4-php/

geturl:

https://gist.github.com/anthonyeden/4448695ad531016ec12bcdacc9d91cb8

Sign up to request clarification or add additional context in comments.

3 Comments

@UmairHamid: You found a solution for this?
Yes @ashishmishra
@ashishmishra Please check my answer in this thread
1

I wrote a function in php using @xmxmxmx answer and it is working fine with me

function AWS_S3_PresignDownload($AWSAccessKeyId, $AWSSecretAccessKey, $BucketName, $AWSRegion, $canonical_uri, $expires = 8400)
{
    $encoded_uri = str_replace('%2F', '/', rawurlencode($canonical_uri));
    // Specify the hostname for the S3 endpoint
    if ($AWSRegion == 'us-east-1') {
        $hostname = trim($BucketName . ".s3.amazonaws.com");
        $header_string = "host:" . $hostname . "\n";
        $signed_headers_string = "host";
    } else {
        $hostname =  trim($BucketName . ".s3-" . $AWSRegion . ".amazonaws.com");
        $header_string = "host:" . $hostname . "\n";
        $signed_headers_string = "host";
    }

    $currentTime = time();
    $date_text = gmdate('Ymd', $currentTime);

    $time_text = $date_text . 'T' . gmdate('His', $currentTime) . 'Z';
    $algorithm = 'AWS4-HMAC-SHA256';
    $scope = $date_text . "/" . $AWSRegion . "/s3/aws4_request";

    $x_amz_params = array(
        'X-Amz-Algorithm' => $algorithm,
        'X-Amz-Credential' => $AWSAccessKeyId . '/' . $scope,
        'X-Amz-Date' => $time_text,
        'X-Amz-SignedHeaders' => $signed_headers_string
    );

    // 'Expires' is the number of seconds until the request becomes invalid
    $x_amz_params['X-Amz-Expires'] = $expires + 30; // 30seocnds are less
    ksort($x_amz_params);

    $query_string = "";
    foreach ($x_amz_params as $key => $value) {
        $query_string .= rawurlencode($key) . '=' . rawurlencode($value) . "&";
    }
    
    $query_string = substr($query_string, 0, -1);

    $canonical_request = "GET\n" . $encoded_uri . "\n" . $query_string . "\n" . $header_string . "\n" . $signed_headers_string . "\nUNSIGNED-PAYLOAD";
    $string_to_sign = $algorithm . "\n" . $time_text . "\n" . $scope . "\n" . hash('sha256', $canonical_request, false);

    $signing_key = hash_hmac('sha256', 'aws4_request', hash_hmac('sha256', 's3', hash_hmac('sha256', $AWSRegion, hash_hmac('sha256', $date_text, 'AWS4' . $AWSSecretAccessKey, true), true), true), true);

    $signature = hash_hmac('sha256', $string_to_sign, $signing_key);
    return 'https://' . $hostname . $encoded_uri . '?' . $query_string . '&X-Amz-Signature=' . $signature;
}

Call it using

echo AWS_S3_PresignDownload('accessId', 'seceret', 's3BucketName', 'reGion', '/fileKey.ext', 60);

Comments

0

Creating an S3 pre-signed URL is actually very easy for GET requests. PUT is fairly easy, but POST is complicated and requires a policy.

The challenge is creating the signing code. Amazon supports two versions v2 and v4. v2 is being phased out. v4 is somewhat complicated to code.

If you are only creating pre-signed URLs for GET requests, write your own code. For anything else, I seriously recommend using the SDK.

Below is a link to the source code to pre-sign an S3 URL using S3V4 using PHP without the AWS SDK.

S3LINK-V4.PHP

3 Comments

I solved it by using some functions and making a lightweight class, the API is really bloated.
I'm accepting your answer because you bothered. I actually used a modified code based on that one you just posted so you are correct, thanks.
@xmxmxmx Hi, can you share it, please.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.