I am writing a webhook in bref and want it to send a message to SQS. Using the entire AWS SDK for this is a colossal waste. How could I calculate the signature?
Asked
Active
Viewed 938 times
3
-
Does this answer your question? [Signature Version 4 Signing Process in PHP to access API Gateway endpoint](https://stackoverflow.com/questions/39822768/signature-version-4-signing-process-in-php-to-access-api-gateway-endpoint) – luk2302 Apr 12 '21 at 07:10
-
Nope, that's inscrutable, that uses `php://input` which it parses into some `$obj` but it's not clear what `$obj->method` and `$obj->data` are. It also uses a security token without explanation. It also has a syntax error (!) because `}else{` is never closed. But `$param` is only defined inside that `else`. – chx Apr 12 '21 at 18:28
-
Use the official AWS SDK - as described here: https://stackoverflow.com/a/73090769/7518989 – RH. Aug 02 '22 at 06:31
-
2"Using the entire AWS SDK for this is a colossal waste. " – chx Aug 02 '22 at 06:46
1 Answers
3
const AWS_DATETIME_FORMAT = 'Ymd\THis\Z';
$url = getenv('SQS_URL');
$data = [
'Action' => 'SendMessage',
'MessageBody' => $body,
'Expires' => (new DateTime('UTC'))->add(new DateInterval('PT1M'))->format(AWS_DATETIME_FORMAT),
'Version' => '2012-11-05',
];
$result = Requests::post($url, sign_request($url, $data), $data);
function sign_request($url, $data) {
// These values are provided by AWS Lambda itself.
$secret_key = getenv('AWS_SECRET_ACCESS_KEY');
$access_key = getenv('AWS_ACCESS_KEY_ID');
$token = getenv('AWS_SESSION_TOKEN');
$region = getenv('AWS_REGION');
$service = 'sqs';
$current = new DateTime('UTC');
$current_date_time = $current->format(AWS_DATETIME_FORMAT);
$current_date = $current->format('Ymd');
$signed_headers = [
'content-type' => 'application/x-www-form-urlencoded',
'host' => "sqs.$region.amazonaws.com",
'x-amz-date' => $current_date_time,
'x-amz-security-token' => $token, // leave this one out if you have a IAM created fixed access key - secret pair and do not need the token.
];
$signed_headers_string = implode(';', array_keys($signed_headers));
$canonical = [
'POST',
parse_url($url, PHP_URL_PATH),
'', // this would be the query string but we do not have one.
];
foreach ($signed_headers as $header => $value) {
$canonical[] = "$header:$value";
}
$canonical[] = ''; // this is always an empty line
$canonical[] = $signed_headers_string;
$canonical[] = hash('sha256', http_build_query($data));
$canonical = implode("\n", $canonical);
$credential_scope = [$current_date, $region, $service, 'aws4_request'];
$key = array_reduce($credential_scope, fn ($key, $credential) => hash_hmac('sha256', $credential, $key, TRUE), 'AWS4' . $secret_key);
$credential_scope = implode('/', $credential_scope);
$string_to_sign = implode("\n", [
'AWS4-HMAC-SHA256',
$current_date_time,
$credential_scope,
hash('sha256', $canonical),
]);
$signature = hash_hmac('sha256', $string_to_sign, $key);
unset($signed_headers['host']);
$signed_headers['Authorization'] = "AWS4-HMAC-SHA256 Credential=$access_key/$credential_scope, SignedHeaders=$signed_headers_string, Signature=$signature";
return $signed_headers;
}
Note this code is using rmccue/requests
to do a POST request.
My serverless.yml
looks like:
provider:
name: aws
region: us-east-1
runtime: provided.al2
environment:
SQS_URL: !Ref BadgeQueue
resources:
Resources:
BadgeQueue:
Type: AWS::SQS::Queue
Properties:
RedrivePolicy:
maxReceiveCount: 3
deadLetterTargetArn: !GetAtt BadgeDeadLetterQueue.Arn
BadgeDeadLetterQueue:
Type: AWS::SQS::Queue
Properties:
MessageRetentionPeriod: 1209600

chx
- 11,270
- 7
- 55
- 129
-
I can't get this to work. I've entered my details and tried all combinations but it failed each time. I've pasted my 'redacted` code here and each time I paste the header results into Postman I get: **The request signature we calculated does not match the signature you provided** Here is my code: https://goonlinetools.com/snapshot/code/#ywzptt16n4stu0fmimefai can someone help please! – Jason Jun 08 '23 at 10:32