

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

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