AWS-SNS-Verify

 view release on metacpan or  search on metacpan

author.t/01_verify.t  view on Meta::CPAN

"Signature" : "DRcI8zgRFKXD/N679D3v9q8uYEt0HYJYUNQoGsNZ3JF6x5mmoEG2e9u+5MwwS2tkOsvwQQZg3vfM8bpiNMcvzqIruZPA4b+MRjyHOPqHEPMmIeM8VsZaqJJVSXErQp/q9xJka6JNOzIKA34TjR5WaDJjuHBgNVaftimlPvpeqKTWSQ9UPdw0wh9Fj1fDlGqr8eVs9LhhAx7EKSNgG1lNuJykf5x4fMq/3SQv30wmtQZ...
"SigningCertURL" : "https://sns.us-east-1.amazonaws.com/SimpleNotificationService-6aad65c2f9911b05cd53efda11f913f9.pem", 
"UnsubscribeURL" : "https://sns.us-east-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-1:041977924901:test:5b4ab24c-a248-4b55-84ef-7143a86f483f" 
} 
END
$sns = AWS::SNS::Verify->new(body => $unicode, certificate_string => $cert_string);
ok $sns->verify, 'does unicode message check out';



note "Tampered body doesn't validate";

my $tampered_body = <<END;
{ 
"Type" : "Notification", 
"MessageId" : "1eea0adc-aa9b-5cac-b812-5c933b245eab", 
"TopicArn" : "arn:aws:sns:us-east-1:041977924901:test", 
"Message" : "TAMPERED MESSAGE", 
"Timestamp" : "2019-11-20T19:09:51.151Z", 
"SignatureVersion" : "1", 
"Signature" : "mztEsPesQ3OGqhYh2nv1iYPZdsYPUY8bFt5AzbxnbLYwUI5r+tOeQem1slr8LydSATRN0aZfqHW5rl4FXuLLQhtkuhZ2qOony8H6kKTGGf5QXWCIPN/ge3V1hfB+GNpAd2UNg9XqLt3PKZHrlW4aLLHixCUf0Baf/6lfQhHLHyp/HcVjm0VCp1s3MSMBZaFHoQTNoBf/Ur7xUvwYH2OvQNMn854yRnlpa85VI/RABZ7...

author.t/01_verify.t  view on Meta::CPAN

my $tampered_body_sns = AWS::SNS::Verify->new(body => $tampered_body, certificate_string => $cert_string);
throws_ok(
    sub { $tampered_body_sns->verify },
    #qr/Could not verify the SES message/,
    'Ouch',
    "Tampered with body doesn't valiate",
);



note "Invalid cert doesn't validate";

my $invalid_cert_sns = AWS::SNS::Verify->new(body => $body, certificate_string => "Nopes");
throws_ok(
    sub { $invalid_cert_sns->verify },
    qr/FATAL: invalid or unsupported RSA key format/,
    "Invalid cert doesn't valiate",
);



lib/AWS/SNS/Verify.pm  view on Meta::CPAN


has certificate => (
    is          => 'ro',
    lazy        => 1,
    default     => sub {
        my $self = shift;
        return Crypt::PK::RSA->new(\$self->certificate_string);
    }
);

has validate_signing_cert_url => (
    is      => 'ro',
    lazy    => 1,
    default => 1,
);

sub fetch_certificate {
    my $self = shift;
    my $url = $self->valid_cert_url($self->message->{SigningCertURL});
    my $response = HTTP::Tiny->new->get($url);
    if ($response->{success}) {

lib/AWS/SNS/Verify.pm  view on Meta::CPAN

    return 1;
}

# See also:
# https://github.com/aws/aws-php-sns-message-validator/blob/master/src/MessageValidator.php#L22
sub valid_cert_url {
    my $self = shift;
    my ($url_string) = @_;
    $url_string ||= '';

    return $url_string unless $self->validate_signing_cert_url;

    my $url = URI::URL->new($url_string);
    unless ( $url->can('host') ) {
        ouch 'Bad SigningCertURL', "The SigningCertURL ($url_string) isn't a valid URL", $self;
    }
    my $host = $url->host;

    # Match all regional SNS endpoints, e.g.
    # sns.<region>.amazonaws.com        (AWS)
    # sns.us-gov-west-1.amazonaws.com   (AWS GovCloud)

lib/AWS/SNS/Verify.pm  view on Meta::CPAN

=head1 SYNOPSIS

 my $body = request->body; # example fetch raw body from Dancer
 my $sns = AWS::SNS::Verify->new(body => $body);
 if ($sns->verify) {
     return $sns->message;
 }

=head1 DESCRIPTION

This module will parse a message from Amazon Simple Notification Service and validate its signature. This way you know the message came from AWS and not some third-party. More info here: L<http://docs.aws.amazon.com/sns/latest/dg/SendMessageToHttp.ve...

=head1 METHODS

=head2 new

Constructor.

=over

=item body

lib/AWS/SNS/Verify.pm  view on Meta::CPAN

        "AWS.SNS.MOBILE.MPNS.NotificationClass" : {"Type":"String","Value":"realtime"}
    }
 }

=item certificate_string

By default AWS::SNS::Verify will fetch the certificate string by issuing an HTTP GET request to C<SigningCertURL>. The SigningCertURL in the message must be a AWS SNS endpoint.

If you wish to use a cached version, then pass it in.

=item validate_signing_cert_url (default: true)

If you're using a fake SNS server in your local test environment, the SigningCertURL won't be an AWS endpoint. If so, set validate_signing_cert_url to 0.

Don't ever do this in any kind of Production environment.

=back

=head2 verify

Returns a 1 on success, or die with an L<Ouch> on a failure.

=head2 message

t/01_verify.t  view on Meta::CPAN

my $sns = AWS::SNS::Verify->new(body => $body, certificate_string => $cert_string);

isa_ok($sns, 'AWS::SNS::Verify');

#is($sns->certificate_string, $sns->fetch_certificate, 'loading the certificate ok');

ok $sns->verify, 'does message check out';



note "Tampered body doesn't validate";

my $tampered_body = <<END;
{
    "Type" : "Notification",
    "MessageId" : "a890c547-5d98-55e2-971d-8826fff56413",
    "TopicArn" : "arn:aws:sns:us-east-1:041977924901:foo",
    "Subject" : "test subject",
    "Message" : "TAMPERED MESSAGE",
    "Timestamp" : "2015-02-20T20:59:25.401Z",
    "SignatureVersion" : "1",

t/01_verify.t  view on Meta::CPAN

        "AWS.SNS.MOBILE.WNS.Type" : {"Type":"String","Value":"wns/badge"},
        "AWS.SNS.MOBILE.MPNS.NotificationClass" : {"Type":"String","Value":"realtime"}
    }
}
END

my $tampered_body_sns = AWS::SNS::Verify->new(body => $tampered_body, certificate_string => $cert_string);
throws_ok(
    sub { $tampered_body_sns->verify },
    qr/Could not verify the SNS message/,
    "Tampered with body doesn't validate",
);



note "Invalid cert doesn't validate";

my $invalid_cert_sns = AWS::SNS::Verify->new(body => $body, certificate_string => "Nopes");
throws_ok(
    sub { $invalid_cert_sns->verify },
    qr/FATAL: invalid or unsupported RSA key format/,
    "Invalid cert doesn't valiate",
);



t/02_valid_cert_url.t  view on Meta::CPAN


my $valid_china_url = "https://sns.cn-north-1.amazonaws.com.cn/SimpleNotificationService-3242342098.pem";
is(
    $sns->valid_cert_url($valid_china_url),
    $valid_china_url,
    "Valid China url returns valid url",
);



my $no_validate_sns = AWS::SNS::Verify->new(body => '', validate_signing_cert_url => 0);
my $test_server_url = "http://my.local.test.server/cert.pem";
is(
    $no_validate_sns->valid_cert_url($test_server_url),
    $test_server_url,
    "Accept any URL if no validation",
);




done_testing();



( run in 0.484 second using v1.01-cache-2.11-cpan-a5abf4f5562 )