Skip to content

Instantly share code, notes, and snippets.

@rafpro
Forked from anthonyeden/AWS_S3_Presign_Download.php
Created February 17, 2022 15:27
Show Gist options
  • Select an option

  • Save rafpro/ff20c120db53865adba427e376876c52 to your computer and use it in GitHub Desktop.

Select an option

Save rafpro/ff20c120db53865adba427e376876c52 to your computer and use it in GitHub Desktop.
AWS S3: Pre-sign Upload & Download Requests [PHP]
<?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;
}
?>
<?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
);
}
?>
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