Last active
December 5, 2023 09:48
-
-
Save anthonyeden/4448695ad531016ec12bcdacc9d91cb8 to your computer and use it in GitHub Desktop.
AWS S3: Pre-sign Upload & Download Requests [PHP]
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
function AWS_S3_PresignDownload($AWSAccessKeyId, $AWSSecretAccessKey, $BucketName, $AWSRegion, $canonical_uri, $expires = 8400) { | |
// Creates a signed download link for an AWS S3 file | |
// Based on https://gist.github.com/kelvinmo/d78be66c4f36415a6b80 | |
$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"; | |
} | |
$date_text = gmdate('Ymd', time()); | |
$time_text = $date_text . 'T000000Z'; | |
$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 | |
); | |
if ($expires > 0) { | |
// 'Expires' is the number of seconds until the request becomes invalid | |
$x_amz_params['X-Amz-Expires'] = $expires; | |
} | |
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; | |
} | |
?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
function AWS_S3_hmac_sha256($key, $msg, $binary = true) { | |
return hash_hmac("sha256", $msg, $key, $binary); | |
} | |
function AWS_S3_PresignUpload($BucketName, $AWSAccessKeyId, $AWSSecretAccessKey, $AWSRegion, $UploadFilenameStartsWith) { | |
/* Function to presign an AWS S3 file upload. | |
This method of uploading can allow clients to securely upload files | |
directly to S3, while ensuring certian conditions are enforced (e.g. upload filename) | |
Written by Anthony Eden http://mediarealm.com.au/ | |
*/ | |
$AWSService = "s3"; | |
$AWSRequest = "aws4_request"; | |
$date = date("Ymd"); | |
$AWSPolicy = '{ "expiration": "'.gmdate("Y-m-d", strtotime("tomorrow")).'T12:00:00.000Z", | |
"conditions": [ | |
{"bucket": "'.$BucketName.'"}, | |
["starts-with", "$key", "'.$UploadFilenameStartsWith.'"], | |
{"x-amz-server-side-encryption": "AES256"}, | |
{"x-amz-credential": "'.$AWSAccessKeyId.'/'.$date.'/'.$AWSRegion.'/'.$AWSService.'/'.$AWSRequest.'"}, | |
{"x-amz-algorithm": "AWS4-HMAC-SHA256"}, | |
{"x-amz-date": "'.$date.'T000000Z" } | |
] | |
}'; | |
$StringToSign = base64_encode($AWSPolicy); | |
$DateKey = AWS_S3_hmac_sha256("AWS4" . $AWSSecretAccessKey, $date); | |
$DateRegionKey = AWS_S3_hmac_sha256($DateKey, $AWSRegion); | |
$DateRegionServiceKey = AWS_S3_hmac_sha256($DateRegionKey, $AWSService); | |
$SigningKey = AWS_S3_hmac_sha256($DateRegionServiceKey, $AWSRequest); | |
$Signature = AWS_S3_hmac_sha256($SigningKey, $StringToSign, false); | |
return array( | |
"BucketName" => $BucketName, | |
"KeyPrefix" => $UploadFilenameStartsWith, | |
"x-amz-server-side-encryption" => "AES256", | |
"X-Amz-Credential" => $AWSAccessKeyId.'/'.$date.'/'.$AWSRegion.'/'.$AWSService.'/'.$AWSRequest, | |
"X-Amz-Algorithm" => "AWS4-HMAC-SHA256", | |
"X-Amz-Date" => $date.'T000000Z', | |
"Policy" => $StringToSign, | |
"X-Amz-Signature" => $Signature | |
); | |
} | |
?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
import requests | |
def uploadS3(srcFilename, S3Upload): | |
# This method uploads a file to a S3 bucket using pre-signed credentials | |
# S3Upload is a dictionary returned by AWS_S3_Presign_Upload.php | |
# Determine the extension of the original file | |
filename, file_extension = os.path.splitext(srcFilename) | |
# Perform the upload | |
r = requests.post( | |
'http://' + S3Upload['BucketName'] + '.s3.amazonaws.com/', | |
files = { | |
'file': open(srcFilename, 'rb') | |
}, | |
data = { | |
"key": S3Upload['KeyPrefix'] + file_extension, | |
"x-amz-server-side-encryption": S3Upload['x-amz-server-side-encryption'], | |
"X-Amz-Algorithm": S3Upload['X-Amz-Algorithm'], | |
"X-Amz-Credential": S3Upload['X-Amz-Credential'], | |
"X-Amz-Date": S3Upload['X-Amz-Date'], | |
"Policy": S3Upload['Policy'], | |
"X-Amz-Signature": S3Upload['X-Amz-Signature'] | |
} | |
) | |
if r.status_code == 200 or r.status_code == 204: | |
# Success! | |
return True | |
else: | |
# Debug output | |
print "ERROR: Cannot upload file to S3", srcFilename | |
print r.status_code, r.reason | |
print r.text | |
return False |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I found some relevant documentation here:
https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html