Tag: SNS


Receiving Amazon SNS Messages in PHP

by Jonathan Eskew | on | in PHP | Permalink | Comments |  Share

A little over a year ago, we announced a new feature in the AWS SDK for PHP that allowed customers to validate inbound SNS messages. In the latest version of the SDK, this functionality is now available in its own package, one that does not depend on the SDK, so it’s simpler to listen to SNS topics with lightweight web services. In this blog post, I’ll show you how to use the Amazon SNS message validator for PHP to parse messages in a single-file web application.

About SNS

Amazon Simple Notification Service (Amazon SNS) is a fast and fully managed push messaging service that can deliver messages over email, SMS, mobile push, and HTTP/HTTPS endpoints.

With Amazon SNS, you can set up topics to publish custom messages to subscribed endpoints. SNS messages are used by many of the other AWS services to communicate information asynchronously about your AWS resources. Some examples include:

  • Configuring Amazon Glacier to notify you when a retrieval job is complete.
  • Configuring AWS CloudTrail to notify you when a new log file has been written.
  • Configuring Amazon Elastic Transcoder to notify you when a transcoding job changes status (e.g., from “Progressing” to “Complete”)

Receiving SNS Messages and Verifying Their Signature

Using the SNS Message Validator’s Message class, you can easily parse raw POST data from SNS:

<?php

require 'path/to/vendor/autoload.php';

$message = AwsSnsMessage::fromRawPostData();
echo $message->get('Message');

Amazon SNS sends different types of messages, including SubscriptionConfirmation, Notification, and UnsubscribeConfirmation. The formats of these messages are described in the Appendix: Message and JSON Formats section of the Amazon SNS Developer Guide.

Messages from Amazon SNS are signed. As a best practice, you should use the MessageValidator class to verify the signature and ensure a message was sent from Amazon SNS.

<?php

use AwsSnsMessage;
use AwsSnsMessageValidator;

$message = Message::fromRawPostData();

// Validate the message
$validator = new MessageValidator();
$validator->validate($message);

Instances of AwsSnsMessageValidator have two methods for validating messages, both of which take an instance of AwsSnsMessage as their only argument. validate (shown above) will throw an AwsSnsExceptionInvalidSnsMessageException. isValid will return a Boolean — true for valid messages and false for invalid ones.

Confirming a Subscription to a Topic

In order for an HTTP(S) endpoint to receive messages, it must first be subscribed to an SNS topic. Subscriptions are confirmed over HTTP(S), so you’ll need to create and deploy an endpoint before you set up a subscription. SubscriptionConfirmation messages provide a URL you can use to confirm the subscription.

$message = AwsSnsMessage::fromRawPostData();

// Validate the message
$validator = new MessageValidator();
if ($validator->isValid($message)) {
    file_get_contents($message->get('SubscribeURL'));
}

Handling Notifications

Let’s put it all together and add some extra code for handling both notifications and subscription control messages.

<?php

if ('POST' !== $_SERVER['REQUEST_METHOD']) {
    http_response_code(405);
    die;
}

require 'path/to/vendor/autoload.php';

try {
    $message = AwsSnsMessage::fromRawPostData();
    $validator = new AwsSnsMessageValidator();
    $validator->validate($message);

    if (in_array($message['Type'], ['SubscriptionConfirmation', 'UnsubscribeConfirmation']) {
        file_get_contents($message['SubscribeURL']);
    }

    $log = new SplFileObject('../messages.log', 'a');
    $log->fwrite($message['Message'] . "n");
} catch (Exception $e) {
    http_response_code(404);
    die;
}

Conclusion

As you can see, receiving, verifying, and handling Amazon SNS messages is simple. Setting up your application to receive SNS messages will allow you to create applications that can handle asynchronous communication from AWS services and other parts of your application.

Verifying Amazon SNS Message Authenticity

by Trevor Rowe | on | in Ruby | Permalink | Comments |  Share

You can now use version 2 of the AWS SDK for Ruby to verify the signatures of Amazon SNS messages. To help prevent spoofing attacks, you should verify messages are sent by Amazon SNS.

The new verifier follows the documented best practices for verification, including:

  • Always use HTTPS when getting the certificate from Amazon SNS.
  • Validate the authenticity of the certificate.
  • Verify the certificate was received from Amazon SNS.

Basic Usage

Usage is straightforward. Construct a message verifier and call one of two methods. The given message body should be the JSON document string of the message.

verifier = Aws::SNS::MessageVerifier.new

verifier.authentic?(message_body)
#=> returns true or false

verifier.authenticate!(message_body)
#=> returns true or raises a VerificationError

You can use one instance of Aws::SNS::MessageVerifier.new to verify multiple messages.

Feedback

As always, we love to hear your feedback. It helps us prioritize our development efforts. In fact, this feature was added by customer request. Feel free to join our Gitter channel or open a GitHub issue.

Subscribing Websites to Amazon SNS Topics

by Norm Johanson | on | in .NET | Permalink | Comments |  Share

Amazon SNS allows you to create topics that have many different subscribers to receive the messages sent from the topic. Amazon SQS queues and emails are probably the most common type of consumers for a topic, but it is also possible to subscribe a website.

Setting Up the Website

The sample application creates a generic handler called SNSReceiver.ashx to handle requests coming from SNS. We’ll discuss each part of the SNSReceiver.ashx individually, but you can download a full copy of SNSReceiver.ashx here.

Each SNS message is sent to the website as an HTTP POST request, which the ProcessRequest method uses to determine if it is is an SNS message that should be processed. For HTTP GET requests, we’ll write back status information of the messages received from SNS.

public void ProcessRequest(HttpContext context)
{
    if(context.Request.HttpMethod == "POST")
    {
        ProcessPost(context);
    }
    else if (context.Request.HttpMethod == "GET")
    {
        ProcessGet(context);
    }
}

SNS messages are sent as JSON documents. In version 2.1.8.0 of the AWS SDK for .NET, we added the utility class Amazon.SimpleNotificationService.Util.Message to parse the JSON. This class also has the ability to verify authenticity of the message coming from SNS. This is done by calling IsMessageSignatureValid. When a subscription is made to a website, the website must confirm the subscription. The confirmation comes into our website like other messages. To detect a confirmation request, we need to check the Type property from the Message object. If the type is SubscriptionConfirmation, then we need to confirm the request; if the type is Notification, then it is a message that needs to be processed.

private void ProcessPost(HttpContext context)
{
    string contentBody;
    using (StreamReader reader = new StreamReader(context.Request.InputStream))
        contentBody = reader.ReadToEnd();

    Message message = Message.ParseMessage(contentBody);

    // Make sure message is authentic
    if (!message.IsMessageSignatureValid())
        throw new Exception("Amazon SNS Message signature is invalid");


    if (message.IsSubscriptionType)
    {
        ConfirmSubscription(context, message);
    }
    else if (message.IsNotificationType)
    {
        ProcessNotification(context, message);
    }
}

To confirm the subscription, we need to call SubscribeToTopic, which uses the SubscribeURL property and makes an HTTP GET request. In a real production situation, you would check the TopicArn property to make sure this is a topic that you should subscribe to.

private void ConfirmSubscription(HttpContext context, Message message)
{
    if (!IsValidTopic(message.TopicArn))
        return;

    try
    {
        message.SubscribeToTopic();
        Trace.WriteLine(string.Format("Subscription to {0} confirmed.", message.TopicArn));
    }
    catch(Exception e)
    {
        Trace.WriteLine(string.Format("Error confirming subscription to {0}: {1}", message.TopicArn, e.Message));
    }
}

To process messages, we grab the MessageText property from the Message object. For demonstration purposes, we add each message to a list of messages that we attach to the Application object. This list of messages is then returned by the GET request handler to display the list of messages received.

private void ProcessNotification(HttpContext context, Message message)
{
    var log = context.Application["log"] as IList;
    if (log == null)
    {
        log = new List();
        context.Application["log"] = log;
    }

    log.Add(string.Format("{0}: Received notification from {1} with message {2}", DateTime.Now, message.TopicArn, message.MessageText));
}

Here is the ProcessGet method that called from ProcessRequest for HTTP GET requests. It shows the list of received messages from SNS.

private void ProcessGet(HttpContext context)
{
    context.Response.ContentType = "text/plain";
    var log = context.Application["log"] as IList;
    if (log == null)
    {
        context.Response.Write("No log messages");
    }
    else
    {
        foreach (var message in log.Reverse())
        {
            context.Response.Write(message + "n");
        }
    }
}

Setting Up a Subscription

Remember that our website must be publicly accessible for SNS to send messages to it. We tested this by first deploying the application to AWS using AWS Elastic Beanstalk. We can use either the AWS Management Console or the AWS Toolkit for Visual Studio. Let’s use the Toolkit to test this.

First, we need to create the topic. In AWS Explorer, right-click Amazon SNS and select Create Topic, give the topic a name, and click OK.

Double-click on the new topic in the explorer to bring up its view. Click Create New Subscription, select HTTP or HTTPS for the protocol depending on how you deployed your application, and specify the URL to the SNSReceiver.ashx.

Depending on how fast the site responds to the confirmation, you might see a subscription status of "Pending Confirmation". If that’s the case, then just click the refresh button.

Once the subscription is confirmed, we can a send a test message by clicking the Publish to Topic button, adding some sample text, and clicking OK. Since our website will respond to GET requests by writing out the messages it receives, we can navigate to the website to see if the test message made it.

Now that we have confirmed messages are being sent and received by our website, we can use the AWS SDK for .NET or any of the other AWS SDKs to send messages to our website. Here is a snippet of how to use the .NET SDK to send messages.

var snsClient = new AmazonSimpleNotificationServiceClient(RegionEndpoint.USWest2);
snsClient.Publish(new PublishRequest
{
    TopicArn = topicArn,
    Message = "Test message"
});

Enjoy sending messages, and let us know what you think.

Testing Webhooks Locally for Amazon SNS

by Jeremy Lindblom | on | in PHP | Permalink | Comments |  Share

In a recent post, I talked about Receiving Amazon SNS Messages in PHP. I showed you how to use the SNS Message and MessageValidator classes in the AWS SDK for PHP to handle incoming SNS messages. The PHP code for the webhook is easy to write, but can be difficult to test properly, since it must be deployed to a server in order to be accessible to Amazon SNS. I’ll show you how you can actually test your code locally with the help of a few simple tools.

Testing Tools

To test the code I wrote for the blog post, I used PHP’s built-in web server (available in PHP 5.4 and later) to serve the code locally. I used another tool called ngrok to expose the locally running PHP server to the public internet. Ngrok does this by creating a tunnel to a specified port on your local machine.

You can use PHP’s built-in web server and ngrok on Windows, Linux, and Mac OS X. If you have PHP 5.4+ installed, then the built-in server is ready to use. To install ngrok, use the simple instructions on the ngrok website. I work primarily in OS X, so you may need to modify the commands I use in the rest of this post if you are using another platform.

Setting Up the PHP Code

First, you’ll need the PHP code that will handle the incoming messages. My post about receiving SNS messages provides a complete code example for doing this.

Let’s create a new folder in your home directly to use for this test. We’ll also install Composer, the AWS SDK for PHP, create a directory for the webroot, and create files for the PHP code and a log.

mkdir ~/sns-message-test && cd ~/sns-message-test
curl -sS https://getcomposer.org/installer | php
php composer.phar require aws/aws-sdk-php:~2.6.0
touch messages.log
mkdir web && touch web/index.php

Now take the PHP code from the other blog post and put it in index.php. Here is that same code, but with the require statement needed to load the SDK with our current file structure. I am also going to update the code to log the incoming messages to a file so we can easily see that the messages are being handled correctly.

<?php

require __DIR__ . '/../vendor/autoload.php';

use AwsSnsMessageValidatorMessage;
use AwsSnsMessageValidatorMessageValidator;
use GuzzleHttpClient;

// Make sure the request is POST
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    http_response_code(405);
    die;
}

try {
    // Create a message from the post data and validate its signature
    $message = Message::fromRawPostData();
    $validator = new MessageValidator();
    $validator->validate($message);
} catch (Exception $e) {
    // Pretend we're not here if the message is invalid
    http_response_code(404);
    die;
}

if ($message->get('Type') === 'SubscriptionConfirmation') {
    // Send a request to the SubscribeURL to complete subscription
    (new Client)->get($message->get('SubscribeURL'))->send();
}

// Log the message
$file = new SplFileObject(__DIR__ . '/../messages.log', 'a');
$file->fwrite($message->get('Type') . ': ' . $message->get('Message') . "n");

Creating an Amazon SNS Topic

Before you can perform any tests, you must set up an Amazon SNS topic. You can do this easily in the AWS Management Console by following the Getting Started with Amazon Simple Notification Service guide. This guide also shows how to subscribe to a topic and publish a message, which you will also need to do in a moment.

Setting Up the Server

OK, we have an Amazon SNS topic ready and all of the files we need in place. Now we need to start up the server and make it accessible to Amazon SNS. To do this, create 3 separate terminal windows or tabs, which we will use for 3 separate long-running processes: the server, ngrok, and tailing the messages log.

Launching the PHP Built-in Server

In the first terminal window, use the following command to start up the PHP built-in web server to serve our little test webhook. (Note: you can use a different port number, just make sure you use the same one with ngrok.)

php -S 127.0.0.1:8000 -t web/

This will create some output that looks something like the following:

PHP 5.4.24 Development Server started at Mon Mar 31 11:02:14 2014
Listening on http://127.0.0.1:8000
Document root is /Users/your-user/sns-message-test/web
Press Ctrl-C to quit.

If you access http://127.0.0.1:8000 from your web browser, you will likely see a blank page, but that request will show up in this terminal window. Since our code is set up to respond only to POST requests, we will see the expected behavior of a 405 HTTP code in the response.

[Mon Mar 31 11:02:44 2014] 127.0.0.1:61409 [405]: /

Creating a Tunnel with ngrok

In the second terminal window, use the following command to create an ngrok tunnel to the PHP server. Use the same port as you did in the previous section.

ngrok 8000

That was easy! The output of this command will contain a publicly accessible URL that forwards to your localhost.

Tunnel Status                 online
Version                       1.6/1.5
Forwarding                    http://58565ed9.ngrok.com -> 127.0.0.1:8000
Forwarding                    https://58565ed9.ngrok.com -> 127.0.0.1:8000
Web Interface                 127.0.0.1:4040
# Conn                        1
Avg Conn Time                 36.06ms

ngrok also provides a small web app running on localhost:4040 that displays all of the incoming requests through the tunnel. It also allows you to click a button to replay a request, which is really helpful for testing and debugging your webhooks.

Tailing the Message Logs

Let’s use the third terminal window to tail the log file that our PHP code writes the incoming messages to.

tail -f messages.log

This won’t show anything yet, but once we start publishing Amazon SNS messages to our topic, they should be printed out in this window.

Testing the Incoming SNS Messages

Now that everything is running and wired up, head back to the Amazon SNS console and subscribe the URL provided by ngrok as an HTTP endpoint for your SNS topic.

If all goes well, you should see output similar to the following on each of the 3 terminal windows.

PHP Server:

[Tue Apr  1 08:51:13 2014] 127.0.0.1:50190 [200]: /

ngrok:

POST /                        200 OK

Log:

SubscriptionConfirmation: You have chosen to subscribe to the topic arn:aws:sns:us-west-2:01234567890:sdk-test. To confirm the subscription, visit the SubscribeURL included in this message.

Back in the SNS console, you should see that the subscription has been confirmed. Next, publish a message to the topic to test that normal messages are processed correctly. The output should be similar:

PHP Server:

[Tue Apr  1 10:08:14 2014] 127.0.0.1:51235 [200]: /

ngrok:

POST /                        200 OK

Log:

Notification: THIS IS MY TEST MESSAGE!

Nice work!

Cleaning Up

Now that we are done, be sure to shutdown (Ctrl+C) ngrok, tail, and the local php server. Unsubscribe the defunct endpoint you used for this test, or just delete the SNS topic entirely if you aren’t using it for anything else.

With these tools, you can now test webhooks in your applications locally and interact with Amazon SNS more easily.

Receiving Amazon SNS Messages in PHP

by Jeremy Lindblom | on | in PHP | Permalink | Comments |  Share

The following post details how to use version 2 of the AWS SDK for PHP to receive and validate HTTP(S) messages from Amazon SNS. For a guide on how to do so with version 3 of the SDK, please see our updated post.

Handling inbound Amazon SNS notification messages with PHP is simple. In this post, I’ll show you how to retrieve data from incoming messages, and verify that the messages are coming from Amazon SNS.

A Little About Amazon SNS

Amazon Simple Notification Service (Amazon SNS) is a fast, fully-managed, push messaging service. Amazon SNS can deliver messages to email, mobile devices (i.e., SMS; iOS, Android and FireOS push notifications), Amazon SQS queues, and HTTP/HTTPS endpoints.

With Amazon SNS, you can setup topics to publish custom messages to subscribed endpoints. However, SNS messages are used by many of the other AWS services to communicate information asynchronously about your AWS resources. Some examples include:

  • Configuring Amazon Glacier to notify you when a retrieval job is complete.
  • Configuring AWS CloudTrail to notify you when a new log file has been written.
  • Configuring Amazon Elastic Transcoder to notify you when a transcoding job changes status (e.g., from "Progressing" to "Complete")

Though you can certainly subscribe your email address to receive SNS messages from service events like these, your inbox would fill up rather quickly. There is great power, however, in being able to subscribe an HTTP/HTTPS endpoint to receive the messages. This allows you to program webhooks for your applications to easily respond to various events.

Receiving a Message

In order for an HTTP/HTTPS endpoint to receive messages, you must subscribe the endpoint to an SNS topic. Before you do that, you need to create and deploy a script to the endpoint to process the messages.

Here is a naïvely simple PHP script that can read a posted SNS message.

<?php

// Fetch the raw POST body containing the message
$postBody = file_get_contents('php://input');

// JSON decode the body to an array of message data
$message = json_decode($postBody, true);
if ($message) {
    // Do something with the data
    echo $message['Message'];
}

The AWS SDK for PHP has an SNS Message class for representing an SNS message. It encapsulates the preceding code, and also validates the structure of the message data.

<?php

// Include Composer autoloader
require 'path/to/vendor/autoload.php';

// Create a message object from the POST body
$message = AwsSnsMessageValidatorMessage::fromRawPostData();
echo $message->get('Message');

Amazon SNS sends different types of messages including SubscriptionConfirmation, Notification, and UnsubscribeConfirmation. The formats of these messages are described on the Appendix: Message and JSON Formats section of the Amazon SNS Developer Guide.

Confirming a Subscription to a Topic

In order to handle a SubscriptionConfirmation message, we need to add some code that actually does something with the message. SubscriptionConfirmation messages provide a URL that you can use to confirm the subscription. We’ll use a Guzzle HTTP client to send a GET request to the URL.

$message = AwsSnsMessageValidatorMessage::fromRawPostData();

// Create a Guzzle client and send a request to the SubscribeURL
$client = new GuzzleHttpClient();
$client->get($message->get('SubscribeURL'))->send();

Verifying a SNS Message’s Signature

Messages from Amazon SNS are signed. It’s a good practice to verify the signature and ensure that a message was actually sent from Amazon SNS before performing actions as a result of the message. The SDK includes a MessageValidator class for validating the message, but you must have the OpenSSL PHP extension installed to use it.

use AwsSnsMessageValidatorMessage;
use AwsSnsMessageValidatorMessageValidator;

$message = Message::fromRawPostData();

// Validate the message
$validator = new MessageValidator();
$validator->validate($message);

Handling Notifications

Let’s put it all together and add some extra code for handling both SubscriptionConfirmation and Notification messages.

<?php

require 'path/to/vendor/autoload.php';

use AwsSnsMessageValidatorMessage;
use AwsSnsMessageValidatorMessageValidator;
use GuzzleHttpClient;

// Make sure the request is POST
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    http_response_code(405);
    die;
}

try {
    // Create a message from the post data and validate its signature
    $message = Message::fromRawPostData();
    $validator = new MessageValidator();
    $validator->validate($message);
} catch (Exception $e) {
    // Pretend we're not here if the message is invalid
    http_response_code(404);
    die;
}

if ($message->get('Type') === 'SubscriptionConfirmation') {
    // Send a request to the SubscribeURL to complete subscription
    (new Client)->get($message->get('SubscribeURL'))->send();
} elseif ($message->get('Type') === 'Notification') {
    // Do something with the notification
    save_message_to_database($message);
}

Conclusion

As you can see, receiving, verifying and handling Amazon SNS messages is simple. Setting up your application to receive SNS messages will allow you to create applications that can handle asynchronous communication from AWS services and other parts of your application.

EDIT: My next blog post is a follow up to this one, and describes how you can test your Amazon SNS webhooks locally.

Subscribing an SQS Queue to an SNS Topic

by Norm Johanson | on | in .NET | Permalink | Comments |  Share

In version 2.0.2.3 of the SDK we added an enhancement to the SDK to make it easier to subscribe an Amazon SQS queue to an Amazon SNS topic. You have always been able to subscribe queues to topics using the Subscribe method on the SNS client, but after you subscribed to the topic with your queue, you also had to set a policy on the queue using the SetQueueAttributes method from the SQS client. The policy gives permission to the topic to send a message to the queue.

With this new feature, you can call SubscribeQueue from the SNS client, and it will take care of both the subscription and setting up the policy. This code snippet shows how to create a queue and topic, subscribe the queue, and then send a message.

string queueURL = sqsClient.CreateQueue(new CreateQueueRequest
{
    QueueName = "theQueue"
}).QueueUrl;


string topicArn = snsClient.CreateTopic(new CreateTopicRequest
{
    Name = "theTopic"
}).TopicArn;

snsClient.SubscribeQueue(topicArn, sqsClient, queueURL);

// Sleep to wait for the subscribe to complete.
Thread.Sleep(TimeSpan.FromSeconds(5));

// Publish the message to the topic
snsClient.Publish(new PublishRequest
{
    TopicArn = topicArn,
    Message = "Test Message"
});

// Get the message from the queue.
var messages = sqsClient.ReceiveMessage(new ReceiveMessageRequest
{
    QueueUrl = queueURL,
    WaitTimeSeconds = 20
}).Messages;