• yundream
  • 2018-07-20 07:41:26
  • 2017-01-29 16:40:55
  • 110007

Contents

MAC

MAC는 Message Authentication code(메시지 인증 코드)의 줄임말로, 메시지를 인증하기 위해서 사용하는 정보다.

A 유저가 인터넷으로 연결된 B 유저에게 라고 메시지를 보냈다고 가정해보자. 이 메시지에서 "A"가 보냈다는 것은 매우 중요한 정보를 담고 있으며, 따라서 위조 혹은 변조되면 안 된다. 메시지가 인터넷을 가로지르면 아래의 두 가지 보안 위험에 노출 된다.
  1. 메시지 변조 : 중간에 누군가가 메세지를 가로채서 내용을 변조 해서 전송한다.
  2. 메시지 위조 : 악의 적인 사용자가 올바른 메시지인 것처럼 위조 해서 전송한다.
위의 문제를 해결하기 위해서는 메시지의 무결성과 메시지 인증이 담보되야 한다. 무결성이란 "메시지가 변조되지 않았다는" 성질이며, 메시지 인증 이란 "올바른 송신자로 부터 온 것"이라는 성질이다.

Message Authentication code는 메시지붙이는 작은 정보로 이 것을 이용해서무결성인증문제를 해결한다. MAC 생성 프로세스는 아래와 같다.

 MAC 생성 프로세스

  1. 송신 메시지를 준비한다.
  2. 송신 메시지를 MAC함수 MAC()에 통과 시킨다.
  3. MAC()함수의 매개변수로 Authentication key를 넘긴다. 즉 MAC(Message, key)가 될 것이다.
  4. MAC() 함수는 key와 메시지를 이용해서 MAC 정보를 만들고 이것을 메시지에 붙인다.
  5. 메시지를 수신한다.
  6. 수신측 역시 MAC(Message, key)함수를 이용해서 메시지에 대한 MAC을 만든다.
  7. MAC(Message, key)를 돌려서 나온 MAC과 수신 메시지의 MAC이 일치하면, 이 메시지는 안전한 메시지다.
이를 위해서는 수신측과 송신측이 같은 MAC 함수를 사용해야 하며, Authentication Key(이하 Key)도 일치해야 한다. Key는 메시지와 송신자가 안전한 방법으로 미리 공유해야 할 것이다.

HMAC

Keyed-hash message authentication code(HMAC)은 MAC의 특정한 구현이다. MD5, SHA-1과 같은 해시기반 함수를 이용해서 MAC 정보를 만든다. 어떤 알고리즘을 사용하느냐에 따라서 HMAC-SHA1, HMAC-MD5리고 부른다. 단방향 해시 함수라면 어떤 것이든지 이용 할 수 있다. 따라서 HMAC의 암호 강도는 어떤 크기의 해시를 사용하는지에 따라서 달라진다. 아래 코드는 MAC-MD5, MAC-SHA1, MAC-SHA256 세가지 방식으로 MAC을 만든다.

package main

import (
    "crypto/hmac"
    "crypto/md5"
    "crypto/sha1"
    "crypto/sha256"
    "fmt"
)
const (
   SECRET_KEY = "mysecretKey"
)
func main() {
    input := "Hello. My name is A"
    hmac_md5 := hmac.New(md5.New, []byte(SECRET_KEY))
    hmac_md5.Write([]byte(input))
    hmac_sha1 := hmac.New(sha1.New, []byte(SECRET_KEY))
    hmac_sha1.Write([]byte(input))
    hmac_sha256 := hmac.New(sha256.New, []byte(SECRET_KEY))
    hmac_sha256.Write([]byte(input))

    fmt.Printf("md5:\t%x\n", hmac_md5.Sum(nil))
    fmt.Printf("sha1:\t%x\n", hmac_sha1.Sum(nil))
    fmt.Printf("sha256:\t%x\n", hmac_sha256.Sum(nil))
}

		

HMAC의 취약점

HMAC 적용된 메시지는위/변조를 할 수 없다. 하지만 메시지를 가로챌 수 있으며, 이 경우 목적지로 다시 보내는 replay공격에 노출 될 수 있다. 이 취약점은 아래의 방법으로 대응 할 수 있다.
  1. 메시지에 일련번호를 넣는다. 보통 유닉스 시간(Unix timestamp)을 넣는다.
  2. 메시지는 유효시간을 가진다. 메시지에 있는 유닉스 시간으로 부터 계산 할 수 있다.
아래 코드를 보자.
package main

import (
    "crypto/hmac"
    "crypto/sha1"
    "encoding/binary"
    "fmt"
    "net/url"
    "strconv"
    "strings"
    "time"
)

const (
    SECRET_KEY = "mysecretKey"
)

type macManager struct {
}

func (m macManager) encryptUrl(api string, now int64) string {
    b := make([]byte, 8)
    binary.LittleEndian.PutUint64(b, uint64(now))

    hmac_sha1 := hmac.New(sha1.New, []byte("secret key"))
    hmac_sha1.Write(append([]byte(api)[:], b[:]...))
    mac := hmac_sha1.Sum(nil)

    return fmt.Sprintf("%s?mac=%x#%d", api, mac, now)
}

func (m macManager) check(encryptApi string) bool {
    u, _ := url.Parse(encryptApi)
    apis := strings.Split(encryptApi, "?")
    encryptTime, _ := strconv.ParseInt(u.Fragment, 10, 64)
    if encryptApi == m.encryptUrl(apis[0], encryptTime) {
        return true
    }
    return false
}

func main() {
    MAC := macManager{}
    now := time.Now().Unix()
    macUrl := MAC.encryptUrl("https://www.joinc.co.kr/api/user/profile", now)
    fmt.Println("Request url : ", macUrl)
    if MAC.check(macUrl) {
        fmt.Println("Test OK")
    } else {
        fmt.Println("Test Fail")
    }
}

		
macManager.encryptUrl() 메서드는 URL에 유닉스 시간을 더한 값에, hmac-sha1을 적용했다. 이렇게 만들어진 hmac을 URL 파라메터에 덧 붙여서 (API Gateway 등의)서버에 전송한다. 서버에서는 URL과 시간 값을 이용해서 mac 코드를 만들고 이 값이 요청과 일치하는지 확인하는 것으로 올바른 API 요청인지를 검사 할 수 있다. 또한 API 요청이 만들어진 시간을 확인해서 일정 시간이 지난 API는 요청을 거부하는 것으로 replay 공격을 막을 수 있다.

 유닉스 시간을 HMAC에 적용
  1. 현재 시간을 계산한 값을 URL에 덧붙인다. 예제 코드에서는 byte를 더했는데, string 연산을 해도 상관은 없다.
  2. 인코딩된 mac을 url 파라메터로 넘기고, 시간은 fragment 로 넘겼다.
  3. API Gateway는 url과 fragment를 더해서 MAC 을 만든다. 이 MAC과 url 파라메터의 MAC이 일치하면 올바른 요청이다.
  4. fragment의 유닉스 시간을 이용해서, API에 대한 TTL을 검사 할 수 있다.

응용

  1. API Gateway 에서 요청을 인증하기 위해서 널리 사용한다.
  2. IPsec과 TLS 프로토콜

참고