Skip to content

Instantly share code, notes, and snippets.

@napolux
Created August 21, 2018 05:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save napolux/02e1c28bd976c42fe70d3e5676fc7779 to your computer and use it in GitHub Desktop.
Save napolux/02e1c28bd976c42fe70d3e5676fc7779 to your computer and use it in GitHub Desktop.
<?php
namespace API\Middleware;
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;
/**
* Class AmazonMiddleware
* @package API\Middleware
*/
class AmazonMiddleware
{
// create the folder and give appropriate permissions
const AMZN_CERT_PATH = '/var/cache/amazon-echo-cert/';
public function __invoke(Request $request, Response $response, callable $next) {
$validate = $this->validateAmazonRequest($request);
if ($validate['response'] !== true) {
return $response->withJSON(['Error' => $validate['msg']])->withStatus(400);
}
$response = $next($request, $response);
return $response;
}
/**
* From https://github.com/rbowen/validate-echo-request-php/
*/
private function validateAmazonRequest(Request $request) {
// validate timestamp of request...
if(!$this->validateTimestamp($request->getServerParam('REQUEST_TIME'))) {
return $this->error('Timestamp is not valid');
}
$certificateUrl = $request->getServerParam('HTTP_SIGNATURECERTCHAINURL');
$httpSignature = $request->getServerParam('HTTP_SIGNATURE');
// validate that the request is coming from amazon
if($certificateUrl === null) {
return $this->error('Request not coming from amazon');
}
// validate certificate url
if(!$this->validateCertificateUrl($certificateUrl)) {
return $this->error('Certificate URL is not valid');
}
if(!$this->validateCertificateSignature($request->getBody(), $httpSignature, $certificateUrl)) {
return $this->error('Certificate signature is not valid');
}
return $this->success('Request validated');
}
private function validateCertificateUrl($certificateUrl) {
$urlParts = parse_url($certificateUrl);
if (strcasecmp($urlParts['host'], 's3.amazonaws.com') != 0)
return false;
if (strpos($urlParts['path'], '/echo.api/') !== 0)
return false;
if (strcasecmp($urlParts['scheme'], 'https') != 0)
return false;
if (array_key_exists('port', $urlParts) && $urlParts['port'] != '443')
return false;
return true;
}
private function validateTimestamp($timestamp) {
if (time() - (int)$timestamp > 60) {
// the request is too old...
return false;
} else {
return true;
}
}
private function validateCertificateSignature($requestBody, $httpSignature, $certificateUrl) {
// Determine if we need to download a new Signature Certificate Chain from Amazon
$md5pem = self::AMZN_CERT_PATH . md5($certificateUrl) . '.pem';
$echoServiceDomain = 'echo-api.amazon.com';
// If we haven't received a certificate with this URL before,
// store it as a cached copy
if (!file_exists($md5pem)) {
file_put_contents($md5pem, file_get_contents($certificateUrl));
}
// Validate certificate chain and signature
$pem = file_get_contents($md5pem);
$sslCheck = openssl_verify($requestBody, base64_decode($httpSignature), $pem, 'sha1');
if ($sslCheck != 1) {
return false;
}
// Parse certificate for validations below
$parsedCertificate = openssl_x509_parse($pem);
if (!$parsedCertificate) {
return false;
}
// Check that the domain echo-api.amazon.com is present in
// the Subject Alternative Names (SANs) section of the signing certificate
if(strpos($parsedCertificate['extensions']['subjectAltName'], $echoServiceDomain) === false) {
return false;
}
// Check that the signing certificate has not expired
// (examine both the Not Before and Not After dates)
$validFrom = $parsedCertificate['validFrom_time_t'];
$validTo = $parsedCertificate['validTo_time_t'];
$time = time();
if (!($validFrom <= $time && $time <= $validTo)) {
return false;
}
return true;
}
// Utilities for messages
private function error($msg) {
return ['response' => false, 'msg' => $msg];
}
private function success($msg) {
return ['response' => true, 'msg' => $msg];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment