SigV4 canonical URI 이중 인코딩 — Bedrock 403
Bedrock 은 여러 업체의 LLM 을 API 하나로 호출하게 해주는 AWS 서비스이고, SigV4 는 AWS API 요청에 서명해 신원을 증명하는 표준 서명 방식입니다. 보통은 AWS SDK 가 이 서명을 대신 만들어 줍니다.
브라우저 확장에서 AWS SDK 없이 SigV4 서명을 직접 구현해 Bedrock 을 불렀는데, 계속 403 이 났습니다. 서명 단계는 다 맞는 것 같은데 서버가 서명이 안 맞는다고 합니다.
canonical request URI의 이중 인코딩
SigV4 서명은 요청을 정해진 형식(canonical request)으로 정규화한 뒤 그걸 해싱·서명합니다. 서버도 같은 방식으로 정규화해서 서명을 다시 만들고, 두 개가 같은지 비교합니다. 한 글자라도 다르게 정규화하면 서명이 안 맞아 403 입니다.
문제는 canonical request 의 URI path 였습니다. S3 를 제외한 모든 서비스는 URI path 를 두 번 URI 인코딩해야 합니다. 한 번이 아니라 두 번입니다.
Bedrock 의 호출 경로에는 모델 ID 가 들어갑니다.
/model/us.amazon.nova-lite-v1:0/converse
모델 ID 의 콜론(:)은 URI 인코딩 대상입니다. 한 번 인코딩하면 %3A 가 되고,
canonical request 에서는 그 %3A 의 % 를 다시 인코딩해서 %253A 가 돼야
합니다. 제가 한 번만 인코딩해서 %3A 로 서명했는데, 서버는 %253A 로
서명하니 안 맞았습니다.
내가 서명한 경로: /model/us.amazon.nova-lite-v1%3A0/converse (단일 인코딩)
서버가 기대한 경로: /model/us.amazon.nova-lite-v1%253A0/converse (이중 인코딩)
→ 서명 불일치 → 403
이중 인코딩의 이유
이상하게 들리지만, canonical request 는 이미 URI 인코딩된 경로를 받아서 그걸 또 한 번 인코딩한 값으로 정규화하도록 스펙에 정해져 있습니다. 실제 HTTP 요청에 나가는 경로는 한 번 인코딩된 것이고, 서명 계산에만 두 번 인코딩된 값을 씁니다.
S3 만 예외인데, 키에 슬래시 같은 문자가 많아서 두 번 인코딩하면 경로가 깨지기 때문에 한 번만 합니다. 그래서 “S3 는 단일, 나머지는 이중” 이라는 규칙이 생겼습니다.
SDK 를 쓰면 이걸 알아서 처리해주니 평생 모르고 지나갑니다. 직접 구현하면 정확히 여기서 막힙니다.
직접 구현할 때 확인 순서
SigV4 를 손으로 짜다 403 이 나면 순서대로 봅니다.
- 서명 자체가 틀렸나 — 서명 키 유도(HMAC 체이닝), 서명 스코프(날짜/리전/ 서비스)가 맞는지. 여기가 틀리면 아예 모든 요청이 실패합니다.
- canonical request 가 서버와 다른가 — URI 이중 인코딩, 쿼리 파라미터 정렬, 헤더 소문자화·정렬, 서명 대상 헤더(SignedHeaders) 목록.
특수문자가 든 경로에서만 403 이 나고 단순 경로는 되면, 십중팔구 URI 인코딩 문제입니다. 콜론·공백·슬래시가 든 경로로 테스트해보면 바로 드러납니다.
Bedrock 모델 ID 처럼 콜론이 든 경로가 정확히 여기서 걸립니다.
댓글 0