AlkantarClanX12
Current Path : /home/thanudqk/128shen.com/wp-content/plugins/w3-total-cache/lib/Aws/Aws/Sns/ |
Current File : /home/thanudqk/128shen.com/wp-content/plugins/w3-total-cache/lib/Aws/Aws/Sns/MessageValidator.php |
<?php namespace Aws\Sns; use Aws\Sns\Exception\InvalidSnsMessageException; /** * Uses openssl to verify SNS messages to ensure that they were sent by AWS. */ class MessageValidator { const SIGNATURE_VERSION_1 = '1'; /** * @var callable Callable used to download the certificate content. */ private $certClient; /** @var string */ private $hostPattern; /** * @var string A pattern that will match all regional SNS endpoints, e.g.: * - sns.<region>.amazonaws.com (AWS) * - sns.us-gov-west-1.amazonaws.com (AWS GovCloud) * - sns.cn-north-1.amazonaws.com.cn (AWS China) */ private static $defaultHostPattern = '/^sns\.[a-zA-Z0-9\-]{3,}\.amazonaws\.com(\.cn)?$/'; private static function isLambdaStyle(Message $message) { return isset($message['SigningCertUrl']); } private static function convertLambdaMessage(Message $lambdaMessage) { $keyReplacements = [ 'SigningCertUrl' => 'SigningCertURL', 'SubscribeUrl' => 'SubscribeURL', 'UnsubscribeUrl' => 'UnsubscribeURL', ]; $message = clone $lambdaMessage; foreach ($keyReplacements as $lambdaKey => $canonicalKey) { if (isset($message[$lambdaKey])) { $message[$canonicalKey] = $message[$lambdaKey]; unset($message[$lambdaKey]); } } return $message; } /** * Constructs the Message Validator object and ensures that openssl is * installed. * * @param callable $certClient Callable used to download the certificate. * Should have the following function signature: * `function (string $certUrl) : string $certContent` * @param string $hostNamePattern */ public function __construct( callable $certClient = null, $hostNamePattern = '' ) { $this->certClient = $certClient ?: 'file_get_contents'; $this->hostPattern = $hostNamePattern ?: self::$defaultHostPattern; } /** * Validates a message from SNS to ensure that it was delivered by AWS. * * @param Message $message Message to validate. * * @throws InvalidSnsMessageException If the cert cannot be retrieved or its * source verified, or the message * signature is invalid. */ public function validate(Message $message) { if (self::isLambdaStyle($message)) { $message = self::convertLambdaMessage($message); } // Get the certificate. $this->validateUrl($message['SigningCertURL']); $certificate = call_user_func($this->certClient, $message['SigningCertURL']); if ($certificate === false) { throw new InvalidSnsMessageException( "Cannot get the certificate from \"{$message['SigningCertURL']}\"." ); } // Extract the public key. $key = openssl_get_publickey($certificate); if (!$key) { throw new InvalidSnsMessageException( 'Cannot get the public key from the certificate.' ); } // Verify the signature of the message. $content = $this->getStringToSign($message); $signature = base64_decode($message['Signature']); if (openssl_verify($content, $signature, $key, OPENSSL_ALGO_SHA1) != 1) { throw new InvalidSnsMessageException( 'The message signature is invalid.' ); } } /** * Determines if a message is valid and that is was delivered by AWS. This * method does not throw exceptions and returns a simple boolean value. * * @param Message $message The message to validate * * @return bool */ public function isValid(Message $message) { try { $this->validate($message); return true; } catch (InvalidSnsMessageException $e) { return false; } } /** * Builds string-to-sign according to the SNS message spec. * * @param Message $message Message for which to build the string-to-sign. * * @return string * @link http://docs.aws.amazon.com/sns/latest/gsg/SendMessageToHttp.verify.signature.html */ public function getStringToSign(Message $message) { static $signableKeys = [ 'Message', 'MessageId', 'Subject', 'SubscribeURL', 'Timestamp', 'Token', 'TopicArn', 'Type', ]; if ($message['SignatureVersion'] !== self::SIGNATURE_VERSION_1) { throw new InvalidSnsMessageException( "The SignatureVersion \"{$message['SignatureVersion']}\" is not supported." ); } $stringToSign = ''; foreach ($signableKeys as $key) { if (isset($message[$key])) { $stringToSign .= "{$key}\n{$message[$key]}\n"; } } return $stringToSign; } /** * Ensures that the URL of the certificate is one belonging to AWS, and not * just something from the amazonaws domain, which could include S3 buckets. * * @param string $url Certificate URL * * @throws InvalidSnsMessageException if the cert url is invalid. */ private function validateUrl($url) { $parsed = parse_url($url); if (empty($parsed['scheme']) || empty($parsed['host']) || $parsed['scheme'] !== 'https' || substr($url, -4) !== '.pem' || !preg_match($this->hostPattern, $parsed['host']) ) { throw new InvalidSnsMessageException( 'The certificate is located on an invalid domain.' ); } } }