본문 바로가기

programming/Web

HMAC : API 통신 클라이언트 무결성 검증 방법

HMAC : Hash-based Message Authentication Code

해쉬 기반 메시지 신원확인 코드

 

클라이언트 👉 API 서버에 보내는 "요청자의 신원과 메시지의 무결성을 검증하기 위한 해쉬 문자열"

 

HMAC 만드는 방법

  • HMAC는 인증을 위한 Secret Key와 임의의 길이를 가진 Message를 해시함수를 이용해서 생성
  • 해시함수로 MD5, SHA-256등 일반적인 함수 사용가능
  • 각 알고리즘에 따라 다른 고정 길이의 MAC(=해시문자열 = 코드)이 생성된다

 

만약 중간에 해커가 코드를 가로채서 동일한 요청을 계속 보낸다면 (Reply attack), 이를 방지하기 위해서 MAC을 생성할 때 timestamp를 추가해서 사용하는 방법이 있다

API 서버는 해당 메시지가 생성된 시간을 알 수 있고, 생성된 시간부터 일정 시간 이내의 호출만 정상적인 호출로 인식 가능

serial, nonce 등 다른 값들도 룰에 따라 추가 가능

 

HMAC 생성과 API 통신에서 사용방법

1. (Client) 해시 생성

  • 클라이언트는 SHA-256(SecretKey, Message) = MAC 생성

2. 데이터 전송

  • 생성된 MAC + Message 를 API 서버에 전송, MAC은 HTTP Header 또는 쿼리로 (URL)에  포함된다

3. (Server) 해시 생성

  • API 서버는 클라이언트로부터 전달받은 Message 와 가지고 있던 SecretKey를 이용해 SHA-256(SecretKey, Message) = MAC 생성

4. 해시 비교 

  • API 서버에서 생성된 MAC과 클라이언트로부터 전달받은 MAC의 값이 같은지 비교 👉 무결성판단

 

구현 복잡도

Client_id & Client_secret < HMAC < PKI

 

 

예시

  • AWS S3 REST API 기준 HMAC 서명 생성과검증
  • 서명을 생성하는데 필요한 것은 API 서버가 정의한 룰 + 요청 자체에 담긴 여러 데이터(Message) + SecretKey 이다
  • 다양한 룰이 있는데, DomainTools의 경우 Client_id + timestamp + request_uri 조합한 문자열 + client_secret을 이용한 HMAC 생성
  • 서버도 동일한 방법으로 HMAC을 생성하여 클라이언트로 부터 받은 코드가 유효한지 검사

 

 

  • AWS S3 REST API 기준 인증방법
  • S3 REST API는 서버와 클라이언트 만이 공유하는 SecretKey를 사용하여 생성된 서명(HMAC)으로 매번 클라이언트로부터의 요청이 올바른지 매번 인증한다
  • 서버 - 클라이언트 사이의 요청 - 응답 메시지에 대한 보안은 HTTPS 프로토콜을 사용하는 것으로 자연스럽게 수행된다
  • 서명(Signature)은 단지 클라이언트가 진짜인지, 가짜인지 증명하는 역할만 수행
  • HMAC 생성을 위한 해쉬함수로는 SHA256이 사용된다
  • 생성된 Signature가 포함된 요청의 유효시간은 15분이다. 15분이 경과하면 유효하지 않은 요청으로 간주한다. timestamp의 포맷은 ISO8601에 타임존을 추가하여 정의한다

 

 

 

  • [코드예시-JAVA]
String sharedSecret = "5pKRnC5MGNuqEdKkzYy4MA";
String algorithm = "HmacSHA256";
String message = "2017-11-07T18:00:00+09:00 GET http://jsonobject.com/posts/2017";

// HMAC 생성기 초기화, 공유키, 알고리즘, 원본 메시지 지정
Mac mac = Mac.getInstance(algorithm);
mac.init(new SecretKeySpec(sharedSecret.getBytes(), algorithm));

// Signature 바이트 배열 생성
byte[] signatureBytes = mac.doFinal(message.getBytes()));

// 생성된 Signature를 Base64 문자열로 변환, UMuelgDclhzNZPiNqF6NYkZtJnOFqlgu4i4t+4M1fJs=
String signature = Base64.getEncoder().encodeToString(signatureBytes);

 

  • [코드예시 - Binance API]

Request Body에 담아서 전송할 경우

 

앞에 여러가지 key-value 쌍들은 특정 API 메소드에 필요한 params 값들 + SecretKey -> SHA512로 암호화 -> 나온 결과값이 HMAC => 서명

    $ echo -n "symbol=LTCBTC&side=BUY&type=LIMIT&timeInForce=GTC&quantity=1&price=0.1&recvWindow=5000&timestamp=1499827319559" | openssl dgst -sha256 -hmac "NhqPtmdSJYdKjVHjA7PZj4Mge3R5YNiP1e3UZjInClVN65XAbvqqM6A7H5fATj0j"
    (stdin)= c8db56825ae71d6d79447849e617115f4a920fa2acdcab2b053c4b2838bd6b71

API 요청 메시지 (필요한 params = message + HMAC 서명)

    (HMAC SHA256)
    $ curl -H "X-MBX-APIKEY: vmPUZE6mv9SD5VNHk4HlWFsOr6aKE2zvsw0MuIgwCIPy6utIco14y7Ju91duEh8A" -X POST 'https://api.binance.com/api/v3/order' -d 'symbol=LTCBTC&side=BUY&type=LIMIT&timeInForce=GTC&quantity=1&price=0.1&recvWindow=5000&timestamp=1499827319559&signature=c8db56825ae71d6d79447849e617115f4a920fa2acdcab2b053c4b2838bd6b71'

 

 

'programming > Web' 카테고리의 다른 글

[Express] Post로 JSON 데이터 보내기 (미들웨어 설정)  (0) 2022.08.20
Restfull 웹 API 디자인(1)  (0) 2022.08.20
AWS - EC2(EBS/AZ/ELB)  (0) 2022.04.06
[AWS] IAM 이란? (개념과 실습)  (0) 2022.04.05
API, SDK, Library, Framework  (0) 2022.03.10