HMAC 생성 및 검증

API 호출 간 보안을 위한 검증 방식으로 HMAC을 사용합니다.

언어별로 아래 코드를 참조하여 HMAC 검증 부분을 구현합니다. DEV 환경에서는 "test_secret_key"를 사용하여 테스트 해주시면 됩니다. LIVE 환경의 SECRET_KEY는 연동 시작 시 전달해드립니다.

const moment = require('moment');
const qs = require('qs');
const crypto = require('crypto');

function HmacUtil () {

    this.algorithm = "sha256"
    this.secretKey = "test_secret_key"
    this.expiresIn = 2 * 60 * 1000 // 2minutes
 
    this.hmacDatetime = function() {
      return moment().format("YYYY-MM-DDTHH:mm:ssZ")
    }
    
    this.alphabeticalSort = function(a, b) {
      return a.localeCompare(b);
    }
    
    this.sortedQueryString = function(encodedQueryString) {
      let obj = qs.parse(encodedQueryString);
      return qs.stringify(obj, { sort: this.alphabeticalSort });
    }
    
    this.payloadHash = function(payload) {
      return crypto.createHash(this.algorithm).update(payload,'utf8').digest('hex');
    }
    
    this.stringToSign = function(method, uri, hmacDatetime, queryString, payload) {
      return method + "\n" + uri + "\n" + hmacDatetime + "\n" + this.sortedQueryString(queryString) + "\n" + this.payloadHash(payload)
    }
    
    this.sign = function(stringToSign) {
      rawHmac = crypto.createHmac(this.algorithm, this.secretKey).update(stringToSign).digest('hex');
      return Buffer.from(rawHmac).toString('base64');
    }
    
    this.signature = function(method, uri, hmacDatetime, queryString, payload) {
      return this.sign(this.stringToSign(method, uri, hmacDatetime, queryString, payload));
    }
    
    this.isValid = function(method, uri, hmacDatetime, queryString, payload, signature) {
      let sameSignature = this.signature(method, uri, hmacDatetime, queryString, payload) === signature
      let notExpired = (new Date() - new Date(hmacDatetime)) < this.expiresIn // 2.minutes
      return sameSignature && notExpired
    }
    
}
 

API 호출 시 검증에 필요한 값은 요청 헤더로 전달됩니다. 헤더에서 추출한 signature 와 API 호출 정보로 생성한 signature 가 동일한 경우 유효한 요청입니다.

헤더

설명

X-Hmac-Datetime

HMAC 생성 시간 (signature 생성시에도 포함되는 값)

X-Hmac-Signature

HMAC Signature

signature 생성 예시

아래는 signature 생성에 사용 되는 데이터 예시입니다.

데이터

method

(대문자)

POST

uri

/api/offerwall/reward

hmacDateTime

2020-06-08T16:56:34+09:00

queryString

(빈값)

payload

(raw body)

{"campaign_id":"1","uid":"test_uid","advertising_id":"d2ca81dc-e3bc-4449-aa8b-f7b0f62b76b8","platform":1,"reward":100,"reward_type":0,"ad_name":"테스트 광고명!","repeat_participate_type":0,"click_key":"MTU5MTYwMzA2OTA4ODo-PDp0ZXN0X3VpZDo-PDp1U0hIaE5wOTZQeGpGaDFOUjlRR2NiU0U"}

위 데이터로 signature 를 생성한 결과입니다. (secret_key 는 test_secret_key 사용) 직접 구현한 모듈로 테스트 시 동일한 signature 가 생성 되는지 확인해주세요.

payload로 만든 SHA256 해시값은 https://emn178.github.io/online-tools/sha256.html 등의 해시 생성 툴을 통해서도 확인할 수 있습니다.

처리결과

SHA256(payload)

04dd512aa6c17b5e1f38cc3c2d9f652ea22878d51e5ea483161852f20e85bde9

StringToSign

POST

/api/offerwall/reward

2020-06-08T16:56:34+09:00

04dd512aa6c17b5e1f38cc3c2d9f652ea22878d51e5ea483161852f20e85bde9

HMAC Signature

MDY4MzYwNzc2MWYxZmViMTcxNDczZmYyNzVjY2ZlODMzYTU2OWVmMmI0MzE0N2RkZDBmZGY1MTJlMmEzMjE0Nw==

StringToSign값의 개행 처리에 주의해서 Signature값을 생성해주세요.

샘플 코드

실제 signature 검증과 관련하여 아래의 샘플을 통해 확인하실 수 있습니다.