Вход Регистрация
Файл: sngine-v2.8/Script/includes/libs/AWS/Aws/Sns/MessageValidator.php
Строк: 226
<?php
namespace AwsSns;

use 
AwsSnsExceptionInvalidSnsMessageException;

/**
 * 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|false $certContent`
     * @param string $hostNamePattern
     */
    
public function __construct(
        callable 
$certClient null,
        
$hostNamePattern ''
    
) {
        
$this->certClient $certClient ?: function($certUrl) {
            return @ 
file_get_contents($certUrl);
        };
        
$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$keyOPENSSL_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.'
            
);
        }
    }
}
Онлайн: 2
Реклама