AWS-SNS-Verify

 view release on metacpan or  search on metacpan

MANIFEST  view on Meta::CPAN

# This file was automatically generated by Dist::Zilla::Plugin::Manifest v6.012.
Changes
LICENSE
MANIFEST
META.yml
Makefile.PL
README
author.t/01_verify.t
author.t/unicode.t
dist.ini
lib/AWS/SNS/Verify.pm
t/01_verify.t
t/02_valid_cert_url.t
t/author-pod-coverage.t
t/author-pod-syntax.t

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

"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

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';


my $unicode = <<END;
{ 
"Type" : "Notification", 
"MessageId" : "95841c0a-329b-5910-bcc0-4a45a34aa6c7", 
"TopicArn" : "arn:aws:sns:us-east-1:041977924901:test", 
"Message" : "A Test Banana:\uD83C\uDF4C", 
"Timestamp" : "2019-11-20T19:10:09.894Z", 
"SignatureVersion" : "1", 
"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", 

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

"Timestamp" : "2019-11-20T19:09:51.151Z", 
"SignatureVersion" : "1", 
"Signature" : "mztEsPesQ3OGqhYh2nv1iYPZdsYPUY8bFt5AzbxnbLYwUI5r+tOeQem1slr8LydSATRN0aZfqHW5rl4FXuLLQhtkuhZ2qOony8H6kKTGGf5QXWCIPN/ge3V1hfB+GNpAd2UNg9XqLt3PKZHrlW4aLLHixCUf0Baf/6lfQhHLHyp/HcVjm0VCp1s3MSMBZaFHoQTNoBf/Ur7xUvwYH2OvQNMn854yRnlpa85VI/RABZ7...
"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

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",
);



done_testing();

author.t/unicode.t  view on Meta::CPAN

"TopicArn" : "arn:aws:sns:us-east-1:041977924901:test", 
"Message" : "A Test Banana:\uD83C\uDF4C", 
"Timestamp" : "2019-11-20T19:10:09.894Z", 
"SignatureVersion" : "1", 
"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
my $sns = AWS::SNS::Verify->new(body => $unicode, certificate_string => $cert_string);
ok $sns->verify, 'does unicode message check out';





done_testing();

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

        }
    }
    return join("\n", @parts)."\n";
}

sub decode_signature {
    my $self = shift;
    return decode_base64($self->message->{Signature});
}

sub verify {
    my $self = shift;
    my $pk = $self->certificate;
    unless ($pk->verify_message($self->decode_signature, $self->generate_signature_string, 'SHA1', 'v1.5')) {
        ouch 'Bad SNS Signature', 'Could not verify the SNS message from its signature.', $self;
    }
    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 ||= '';

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

AWS::SNS::Verify - Verifies authenticity of SNS messages.

=head1 VERSION

version 0.0105

=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

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

Returns a hash reference of the decoded L<body> that was passed in to the constructor.

=head2 certificate_string

If you want to cache the certificate in a local cache, then get it using this method.

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


You should never need to call this, it decodes the base64 signature.

=head2 fetch_certificate

You should never need to call this, it fetches the signing certificate.


=head2 generate_signature_string

You should never need to call this, it generates the signature string required to verify the request.

=head2 valid_cert_url

You should never need to call this, it checks the validity of the certificate signing URL per L<https://github.com/aws/aws-php-sns-message-validator/blob/master/src/MessageValidator.php#L22>

=head1 REQUIREMENTS

Requires Perl 5.12 or higher and these modules:

=over

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

    }
}
END

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",

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

    "MessageAttributes" : {
        "AWS.SNS.MOBILE.MPNS.Type" : {"Type":"String","Value":"token"},
        "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",
);



done_testing();



( run in 0.566 second using v1.01-cache-2.11-cpan-5467b0d2c73 )