

# Verifying the signatures of Amazon SNS messages
<a name="sns-verify-signature-of-message"></a>

Amazon SNS uses message signatures to confirm the authenticity of messages sent to your HTTP endpoint. To ensure message integrity and prevent spoofing, you **must** verify the signature before processing any Amazon SNS messages.

**When should you verify Amazon SNS signatures?**

You should verify Amazon SNS message signatures in the following scenarios:
+ When Amazon SNS sends a notification message to your HTTP(S) endpoint.
+ When Amazon SNS sends a confirmation message to your endpoint after a [https://docs.aws.amazon.com/sns/latest/api/API_Subscribe.html](https://docs.aws.amazon.com/sns/latest/api/API_Subscribe.html) or [https://docs.aws.amazon.com/sns/latest/api/API_Unsubscribe.html](https://docs.aws.amazon.com/sns/latest/api/API_Unsubscribe.html) API call.

**Amazon SNS supports two signature versions:**
+ SignatureVersion1 – Uses an SHA1 hash of the message.
+ SignatureVersion2 – Uses an SHA256 hash of the message. This provides stronger security and is the recommended option.

**To correctly verify SNS message signatures, follow these best practices:**
+ Always retrieve the signing certificate using HTTPS to prevent unauthorized interception attacks.
+ Check that the certificate is issued by Amazon SNS.
+ Confirm that the certificate’s chain of trust is valid.
+ The certificate should come from an SNS-signed URL.
+ Don't trust any certificates provided in the message without validation.
+ Reject any message with an unexpected `TopicArn` to prevent spoofing.
+ The AWS SDKs for Amazon SNS provide built-in validation logic, reducing the risk of misimplementation.

# Configuring the message signature version on Amazon SNS topics
<a name="sns-verify-signature-of-message-configure-message-signature"></a>

Configuring the message signature version on Amazon SNS topics allows you to enhance the security and compatibility of your message verification process.

Select between `SignatureVersion`**1** (SHA1) and `SignatureVersion`**2** (SHA256) to control the hashing algorithm used for signing messages. Amazon SNS topics default to `SignatureVersion`**1**. You can configure this setting using the [https://docs.aws.amazon.com/sns/latest/api/API_SetTopicAttributes.html](https://docs.aws.amazon.com/sns/latest/api/API_SetTopicAttributes.html) API action.

Use the following example to set the topic attribute `SignatureVersion` using the AWS CLI:

```
aws sns set-topic-attributes \
    --topic-arn arn:aws:sns:us-east-2:123456789012:MyTopic \
    --attribute-name SignatureVersion \
    --attribute-value 2
```

# Verifying the signature of an Amazon SNS message when using HTTP query-based requests
<a name="sns-verify-signature-of-message-verify-message-signature"></a>

Verifying the signature of an Amazon SNS message when using HTTP query-based requests ensures the message's authenticity and integrity. This process confirms that the message originates from Amazon SNS and has not been tampered with during transit. By parsing the message, constructing the correct string to sign, and validating the signature against a trusted public key, you safeguard your system against spoofing and unauthorized message alterations.

1. Extract **key-value pairs** from the JSON document in the HTTP POST request body sent by Amazon SNS. These fields are required to construct the **string to sign**.
   + `Message`
   + `Subject` (if present)
   + `MessageId`
   + `Timestamp`
   + `TopicArn`
   + `Type`

   For example:

   ```
   MESSAGE_FILE="message.json"
   FIELDS=("Message" "MessageId" "Subject" "Timestamp" "TopicArn" "Type")
   ```
**Note**  
If any field contains escaped characters (for example, `\n`), convert them to their **original form** to ensure an exact match.

1. Locate the `SigningCertURL` field in the Amazon SNS message. This certificate contains the public key needed to verify the message signature. For example:

   ```
   SIGNING_CERT_URL=$(jq -r '.SigningCertURL' "$MESSAGE_FILE")
   ```

1. Ensure the `SigningCertURL` is from a trusted AWS domain (for example, https://sns.us-east-1.amazonaws.com). Reject any URLs **outside AWS domains** for security reasons.

1. Download the **X.509 certificate **from the provided URL. For example:

   ```
   curl -s "$SIGNING_CERT_URL" -o signing_cert.pem
   ```

1. Extract the **public key** from the downloaded X.509 certificate. The public key allows you to decrypt the message's signature and verify its integrity. For example:

   ```
   openssl x509 -pubkey -noout -in signing_cert.pem > public_key.pem
   ```

1. Different message types require different key-value pairs in the string to sign. Identify the **message type** (`Type` field in the Amazon SNS message) to determine which **key-value pairs** to include:
   + **Notification message** – Includes `Message`, `MessageId`, `Subject` (if present), `Timestamp`, `TopicArn`, and `Type`.
   + **SubscriptionConfirmation** or **UnsubscribeConfirmation message** – Includes `Message`, `MessageId`, `SubscribeURL`, `Timestamp`, `Token`, `TopicArn`, and `Type`.

1. Amazon SNS requires the string to sign to follow a strict, fixed field order for verification. **Only the explicitly required fields must be included**—no extra fields can be added. Optional fields, such as `Subject`, must be included only if present in the message and must appear in the exact position defined by the required field order. For example:

   ```
   KeyNameOne\nValueOne\nKeyNameTwo\nValueTwo
   ```
**Important**  
Do not add a newline character at the end of the string.

1. Arrange the** key-value pairs** in byte-sort order (alphabetical by key name).

1. Construct the **string to sign **using the following format example:

   ```
   STRING_TO_SIGN=""
   for FIELD in "${FIELDS[@]}"; do
       VALUE=$(jq -r --arg field "$FIELD" '.[$field]' "$MESSAGE_FILE")
       STRING_TO_SIGN+="$FIELD\n$VALUE"
       # Append a newline after each field except the last one
       if [[ "$FIELD" != "Type" ]]; then
           STRING_TO_SIGN+="\n"
       fi
   done
   ```

   **Notification message example:**

   ```
   Message
   My Test Message
   MessageId
   4d4dc071-ddbf-465d-bba8-08f81c89da64
   Subject
   My subject
   Timestamp
   2019-01-31T04:37:04.321Z
   TopicArn
   arn:aws:sns:us-east-2:123456789012:s4-MySNSTopic-1G1WEFCOXTC0P
   Type
   Notification
   ```

   **SubscriptionConfirmation example:**

   ```
   Message
   Please confirm your subscription
   MessageId
   3d891288-136d-417f-bc05-901c108273ee
   SubscribeURL
   https://sns.us-east-2.amazonaws.com/...
   Timestamp
   2024-01-01T00:00:00.000Z
   Token
   abc123...
   TopicArn
   arn:aws:sns:us-east-2:123456789012:MyTopic
   Type
   SubscriptionConfirmation
   ```

1. The `Signature` field in the message is Base64-encoded. You need to **decode** it to compare its **raw binary form** with the **derived hash**. For example:

   ```
   SIGNATURE=$(jq -r '.Signature' "$MESSAGE_FILE")
   echo "$SIGNATURE" | base64 -d > signature.bin
   ```

1. Use the `SignatureVersion` field to select the hash algorithm:
   + For `SignatureVersion`**1**, use **SHA1** (for example, `-sha1`).
   + For `SignatureVersion`**2**, use **SHA256** (for example, `-sha256`).

1. To confirm the authenticity of the Amazon SNS message, generate a **hash** of the constructed string and verify the signature using the **public key**.

   ```
   openssl dgst -sha256 -verify public_key.pem -signature signature.bin <<< "$STRING_TO_SIGN"
   ```

   If the signature is valid, the output is `Verified OK`. Otherwise, the output is `Verification Failure`.

## Example script with error handling
<a name="sns-verify-signature-of-message-example"></a>

The following example script automates the verification process:

```
#!/bin/bash

# Path to the local message file
MESSAGE_FILE="message.json"

# Extract the SigningCertURL and Signature from the message
SIGNING_CERT_URL=$(jq -r '.SigningCertURL' "$MESSAGE_FILE")
SIGNATURE=$(jq -r '.Signature' "$MESSAGE_FILE")

# Fetch the X.509 certificate
curl -s "$SIGNING_CERT_URL" -o signing_cert.pem

# Extract the public key from the certificate
openssl x509 -pubkey -noout -in signing_cert.pem > public_key.pem

# Define the fields to include in the string to sign
FIELDS=("Message" "MessageId" "Subject" "Timestamp" "TopicArn" "Type")

# Initialize the string to sign
STRING_TO_SIGN=""

# Iterate over the fields to construct the string to sign
for FIELD in "${FIELDS[@]}"; do
    VALUE=$(jq -r --arg field "$FIELD" '.[$field]' "$MESSAGE_FILE")
    STRING_TO_SIGN+="$FIELD\n$VALUE"
    # Append a newline after each field except the last one
    if [[ "$FIELD" != "Type" ]]; then
        STRING_TO_SIGN+="\n"
    fi
done

# Verify the signature
echo -e "$STRING_TO_SIGN" | openssl dgst -sha256 -verify public_key.pem -signature <(echo "$SIGNATURE" | base64 -d)
```