-
[SigV4] 개념, 서명된 요청 생성하기(JS)AWS 2025. 9. 30. 20:00
개요
AWS API 요청에 인증 정보를 추가하는 서명 메커니즘.
요청 주체를 확인하고, 내용 또한 중간에 변조되지 않았다는 것을 증명하기 위한 서명 방식 중 하나이다.
SigV4 서명 프로세스를 이용해서 요청에 Authorization header를 추가하면,
요청을 받는 쪽(AWS 리소스. API Gateway 등)에서도 요청 쪽에서 서명을 만든 것과 같은 원리로 서명을 만들어 서로 비교해 본다. 같으면 요청을 처리한다.
이를 통해 요청이 정확히 그 시각&그 클라이언트가 보낸 것임을 보장하고,
요청 Body까지 포함해서 무결성 확인하며, 요청 Expiration을 제한해 재사용 공격 방지도 가능해진다.
요청에 포함시킬 요소
SigV4로 HTTP/HTTPS요청을 보낼 때는 아래 요소들을 포함해야 한다.
- Endpoint Speciifcation
요청 보낼 DNS name을 명시해야 한다. 주로 서비스 코드, 리전을 포함하는 name이다.
예를 들어 us-east-1리전의 Amazon DynamoDB서비스는 엔드포인트가 dynamodb.us-east-1.com과 같은 형태가 된다.
HTTP/1.1 요청의 경우 꼭 Host 헤더를 포함시켜야 한다.
HTTP/2 요청은 :authority 헤더 또는 Host헤더를 포함시키면 된다. (:authority헤더 권장) - Action
서비스 API action을 명세한다.
예를 들어 DynamoDB에서 CreateTable 하는 Action이라거나
EC2를 DescribeInstance 한다는 등이 있다. - Action parameters
각 API Action에 필요한 파라미터 명시한다. 주로 API Version 등이 쓰인다. - Date
요청 시 date, time을 작성한다.
이는 써드파티가 요청을 인터셉팅해서 나중에 대시 submitting 하는 것을 막아준다.
credentail scope의 date는 request의 date와 무조건 매치되어야 한다.
timestamp는 반드시 UTC, ISO8601 format: YYYYMMDD_T_HHMMSS_Z을 따라야 한다. milliseconds는 포함시키지 않는다. - Authentication information
- Algorithm 서명 프로세스에 사용하는 알고리즘. SigV4의 경우 AWS4-HMAC-SHA256을 사용해서 HMAC-SHA256해시 알고리즘으로 SigV4를 지정한다. - Credentials Access_key 및 자격 증명 범위 등을 연결하여 구성된 문자열. SigV4에서는 액세스 키 ID, YYYYMMDD 형식의 날짜, 리전 코드, 서비스 코드, aws4_request 종료 문자열(슬래시(/)로 구분됨)이 포함된다. (리전 코드, 서비스 코드 및 종료 문자열에는 소문자를 사용해아함) AKIAIOSFODNN7EXAMPLE/YYYYMMDD/region/service/aws4_request - Signed Headers 서명에 포함할 HTTP 헤더. (;)으로 구분된다. ( host;x-amz-date ) 이런식 - Signature 계산된 서명을 나타내는 16진수 인코딩 문자열. Algorithm파라미터에서 지정했던 알고리즘으로 계산해야함.
인증 방법
- HTTP Authorization 헤더를 사용하는 방법
첫 번째 방법으로 HTTP 기본 인증 방식인 Authorization 헤더를 쓸 수 있다.
아래와 같은 형태로 SigV4 헤더를 구성한다.
Authorization과 Credential사이에만 빼고 각 요소를 쉼표(,)로 구분해야 한다.Authorization: AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request, SignedHeaders=host;range;x-amz-date, Signature=fe5f80f77d5fa3beca038a248ff027d0445342fe2855ddc963176630326f1024
- 참고) Authorization 헤더 값 예시

출처: https://docs.aws.amazon.com/ko_kr/IAM/latest/UserGuide/reference_sigv.html - Query String Parameter를 사용하는 방법
두 번째 방법은 전체 요청을 URL로 표현해 버리는 것이다.
이 경우 인증 정보 및 요청 정보를 쿼리 파라미터에 다 포함시키는 것이다. (따라서, 서명된 URL이라고 부르기도 한다)
X-Amz-Expires을 통해 최대 7일까지 유효한 링크를 HTML에 포함시키는 등 할 수 있다.
- 참고) SigV4 서명된 URL 예시
https://s3.amazonaws.com/amzn-s3-demo-bucket/test.txt ? X-Amz-Algorithm=AWS4-HMAC-SHA256 & X-Amz-Credential=<your-access-key-id>/20130721/<region>/s3/aws4_request & X-Amz-Date=20130721T201207Z & X-Amz-Expires=86400 & X-Amz-SignedHeaders=host &X-Amz-Signature=<signature-value>
요청 만들기
앞서 설명한 요소 및 인증방법들을 통해 SigV4 서명된 요청을 생성해서 보내보자.
SDK를 사용하면 훨씬 간단하게 구현 가능하며 권장되는 방식이지만, 학습 차원에서 단계별로 코드를 작성해 본다.
할 일을 3단계로 요약하면 아래와 같다.1. Canonical request 만들기 (앞선 요청 정보들을 기반으로 표준 요청을 만든다.) 2. AWS Credentials를 통해 Signature를 계산한다. 3. Signature를 request의 Authorization header에 넣는다.이 과정을 통해 서명된 요청을 보내면, AWS에서도 동일하게 서명을 계산해서 같은 값이면 액세스를 허용한다.
0. Date, PayloadHash 설정
앞서 SigV4는 요청 Body까지 포함해서 무결성 확인하며, 요청 Expiration을 제한해 재사용 공격 방지도 가능해진다고 했다.
그를 위한 Payload Hash와 요청 시간을 설정하는 코드이다.
// 1. 요청 시간 설정 (UTC) const now = new Date(); const date = now.toISOString().slice(0, 10).replace(/-/g, ''); const xAmzDate = now.toISOString().slice(0, 19).replace(/[-:]/g, '').replace('.', '') + 'Z'; // 2. Payload Hash 계산 const payloadHash = crypto.createHash('sha256').update(body).digest('hex');
1. Canonical Request 생성요청 내용(Host, Action, Header 등)을 표준 형식으로 정렬한다.
Canonical Request는 나중에 String to Sign을 생성하는 데 들어가는 Input 중 하나다.- Canonical Request 구조
이런 식으로 \n 줄 바꿈 문자로 구분해서 연결하는 형태다.<HTTPMethod>\n <CanonicalURI>\n <CanonicalQueryString>\n <CanonicalHeaders>\n <SignedHeaders>\n <HashedPayload>// 3. CANONICAL REQUEST 구성 코드 const headers = { 'host': urlObj.hostname, 'x-amz-content-sha256': payloadHash, 'x-amz-date': xAmzDate }; const canonicalHeaderParts = []; for (const key of Object.keys(headers).sort()) { canonicalHeaderParts.push(`${key}:${headers[key]}`); } const canonicalHeaders = canonicalHeaderParts.join('\n') + '\n'; console.log('canonicalHeaders: ', canonicalHeaders); // 4. Signed Headers 구성 const signedHeaders = Object.keys(headers).sort().join(';'); // 5. Canonical Request 구성 const canonicalRequest = [ method, path, '', // query string 없음 canonicalHeaders, signedHeaders, hashedPayload ].join('\n');
2. 표준 요청(Canonical Request)의 해시 생성
hashedPayload를 생성하는 데 사용한 것과 동일한 알고리즘을 사용하여 canonical request를 해시한다.
(Canonical Request의 해시는 소문자 16진수 문자의 문자열이다.)// 6. hashedCanonicalRequest 생성 const hashedCanonicalRequest = crypto.createHash('sha256').update(canonicalRequest).digest('hex');3. 서명할 문자열(String to Sign) 생성
Canonical Request와 추가 정보(알고리즘, 요청 날짜, 자격 증명 범위, 표준 요청의 해시)를 사용하여 String to Sign을 생성한다.
`Algorithm` \n `RequestDateTime` \n `CredentialScope` \n `HashedCanonicalRequest`* 주의: 맨 끝에는 \n를 쓰면 안 된다.
// 7. String to Sign 구성 const credentialScope = `${date}/${region}/${service}/aws4_request`; const stringToSign = [ 'AWS4-HMAC-SHA256', xAmzDate, credentialScope, hashedCanonicalRequest ].join('\n');
4. 서명 키(Signing Key) 추출시크릿 액세스 키를 사용하여 요청에 서명하는 데 사용되는 키를 파생한다.
DateKey = HMAC-SHA256("AWS4"+"`<SecretAccessKey>`", "`<YYYYMMDD>`") DateRegionKey = HMAC-SHA256(`<DateKey>`, "`<aws-region>`") DateRegionServiceKey = HMAC-SHA256(`<DateRegionKey>`, "`<aws-service>`") SigningKey = HMAC-SHA256(`<DateRegionServiceKey>`, "aws4_request")Key- 시크릿 액세스 키를 포함하는 문자열.Date- 자격 증명 범위에 사용되는 날짜(YYYYMMDD 형식)를 포함하는 문자열.Region- 리전 코드(예:us-east-1)를 포함하는 문자열.Service- 서비스 코드(예:ec2)를 포함하는 문자열.
// 8. Signing Key 생성 const kDate = crypto.createHmac('sha256', 'AWS4' + secretAccessKey).update(date).digest(); const kRegion = crypto.createHmac('sha256', kDate).update(region).digest(); const kService = crypto.createHmac('sha256', kRegion).update(service).digest(); const kSigning = crypto.createHmac('sha256', kService).update('aws4_request').digest();
5. 서명(Signature) 계산파생된 서명 키(Signing Key)를 해시 키로 사용하여, 서명할 문자열(String to Sign)에 Key가 추가된 해시 계산을 한다.
// 9. Signature 계산 const signature = crypto.createHmac('sha256', signingKey).update(stringToSign).digest('hex');
6. 요청에 서명 추가HTTP 헤더 또는 요청의 쿼리 문자열에 계산된 서명을 추가.
나는 Authorization 헤더에 추가하는 방법을 사용했다.아래와 같은 형태를 만들어내야 한다.
Authorization: AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20220830/us-east-1/ec2/aws4_request, SignedHeaders=host;x-amz-date, Signature=`calculated-signature`// 10. Authorization 구성 const authorization = `AWS4-HMAC-SHA256 Credential=${accessKeyId}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;이렇게 만든 Authorization을 최종 요청 헤더에 포함시켜 요청을 보내면 된다.
AWS 측에서는 이렇게 만들어진 요청을 받으면, Body와 요청 시간을 바탕으로 동일한 계산을 진행해서 동일한지 체크한다.
// 11. 최종 요청 헤더 구성 const finalHeaders = { 'content-type': 'application/json', 'Authorization': authorization, 'x-amz-date': xAmzDate, 'x-amz-content-sha256': payloadHash }; console.log("Signed Headers:", finalHeaders); // 12. 서명된 요청 보내기 try { const axiosResponse = await axios({ method: method, url, headers: finalHeaders, data: body, }); console.log("Response status:", axiosResponse.status); console.log("Response data(OUTPUT):", axiosResponse.data); } catch (error) { console.error("Request error:", error.response?.data || error.message); }
참고 자료
https://docs.aws.amazon.com/ko_kr/IAM/latest/UserGuide/reference_sigv.html
'AWS' 카테고리의 다른 글
Amazon S3 Vectors 출시, OpenAI embeddings 넣어보기 (2) 2025.07.24 AWS Summit 2025 후기, Day1 강연 (2) 2025.05.18 - Endpoint Speciifcation