

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

# HTTP クエリベースのリクエストの使用時に Amazon SNS メッセージの署名を確認する
<a name="sns-verify-signature-of-message-verify-message-signature"></a>

HTTP クエリベースのリクエストを使用する際に Amazon SNS メッセージの署名を検証すると、メッセージの信頼性と整合性が保証されます。このプロセスでは、メッセージが Amazon SNS から発信され、転送中に改ざんされていないことを確認します。メッセージの解析、署名用の正しい文字列の作成、信頼できるパブリックキーによる署名の検証を行うことで、なりすましや不正なメッセージ変更からシステムを保護できます。

1. Amazon SNS から送信された HTTP POST リクエストの本文で、JSON ドキュメントから**キーと値**のペアを抽出します。これらのフィールドは、**署名文字列**を作成するために必要です。
   + `Message`
   + `Subject` (存在する場合)
   + `MessageId`
   + `Timestamp`
   + `TopicArn`
   + `Type`

   例えば、次のようになります。

   ```
   MESSAGE_FILE="message.json"
   FIELDS=("Message" "MessageId" "Subject" "Timestamp" "TopicArn" "Type")
   ```
**注記**  
いずれかのフィールドにエスケープ文字 (`\n` など) が含まれている場合は、それらの文字を**元の形式**に変換して、正確な一致が確実に行われるようにします。

1. Amazon SNS メッセージで `SigningCertURL` フィールドを見つけます。この証明書には、メッセージ署名の検証に必要なパブリックキーが含まれています。例えば、次のようになります。

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

1. `SigningCertURL` が信頼された AWS ドメイン ( など) からのものであることを確認しますhttps://sns.us-east-1.amazonaws.com。セキュリティ上の理由から、** AWS ドメイン外の** URLs。

1. 指定された URL から **X.509 証明書**をダウンロードします。例えば、次のようになります。

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

1. ダウンロードした X.509 証明書から**パブリックキー**を抽出します。パブリックキーを使用すると、メッセージの署名を復号化して、その整合性を検証できます。例えば、次のようになります。

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

1. メッセージタイプが異なると、署名文字列に含めるキーと値のペアも異なります。**メッセージタイプ** (Amazon SNS メッセージ内の `Type` フィールド) を特定して、含める**キーと値のペア**を決定します。
   + **通知メッセージ** – `Message`、`MessageId`、`Subject` (存在する場合)、`Timestamp`、`TopicArn`、および `Type` が含まれます。
   + **SubscriptionConfirmation** または **UnsubscribeConfirmation メッセージ** – `Message`、`MessageId`、`SubscribeURL`、`Timestamp`、`Token`、`TopicArn`、および `Type` が含まれます。

1. Amazon SNS では、検証を行うために、署名文字列は厳格で固定されたフィールドの順序に従う必要があります。**明示的な必須フィールドのみを含める必要があり、**追加のフィールドを加えることはできません。`Subject` などのオプションフィールドは、そのフィールドがメッセージ内に存在する場合にのみ含める必要があり、必須フィールドの順序で定義された正確な位置に含める必要があります。例えば、次のようになります。

   ```
   KeyNameOne\nValueOne\nKeyNameTwo\nValueTwo
   ```
**重要**  
文字列の末尾に改行文字を追加しないでください。

1. **キーと値のペア**をバイト順でソートし、並べます (キー名に基づくアルファベット順)。

1. 次の形式の例を使用して、**署名文字列**を作成します。

   ```
   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
   ```

   **通知メッセージの例:**

   ```
   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 の例:**

   ```
   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. メッセージ内の `Signature` フィールドは Base64 でエンコードされています。**未加工のバイナリ形式**と、**派生したハッシュ**を比較するために、このフィールドを**デコード**する必要があります。例えば、次のようになります。

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

1. `SignatureVersion` フィールドを使用してハッシュアルゴリズムを選択します。
   + `SignatureVersion`**1** の場合 **SHA1** を使用します (例: `-sha1`)。
   + `SignatureVersion`**2** の場合 **SHA256** を使用します (例: `-sha256`)。

1. Amazon SNS メッセージの信頼性を確認するには、作成された文字列の**ハッシュ**を生成し、**パブリックキー**を使用して署名を検証します。

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

   署名が有効な場合、出力は `Verified OK` になります。それ以外の場合、出力は `Verification Failure` になります。

## エラー処理を使用したスクリプトの例
<a name="sns-verify-signature-of-message-example"></a>

次のスクリプト例では、検証プロセスを自動化します。

```
#!/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)
```