메뉴

문서정보

목차

signed url

Signed URL은 컨텐츠를 특정 유저에게만 공유하고 싶을 때 사용한다. 일정한 시간동안만 특정유저에게 공유하기 위한 목적으로도 사용 할 수 있다.

내 사이트(Joinc)는 공개된 문서와 그렇지 않은 문서가 있다. 아직 미완성 중인 문서 혹은 공개하기 애매모호한 문서는 로그인해야지만 볼 수 있도록 제한하고 있다. 비공개 문서 중 특정 문서를 누군가에게 공유해야 하는 일이 생기곤 한데, 로그인 할 수 있도록 유저등록을 하면 되지만 매번 유저를 만드는 것도 매우 귀찮다. 하여 Signed URL 기능을 만들기로 했다.

랜덤스트링을 데이터베이스에 저장

공개하고자 하는 URL에 서버에서 생성한 랜덤문자열을 추가하고, 해당 URL을 유저에게 전달하는 방법이 있다. 이 테이블은 아래와 같이 만들 수 있을 것이다. 만들어진 시간 2018/10/18 23:25:05 으로 부터 3600초까지 사용 할 수 있는 URL을 만들었다.

URL Signed Key CreateTime TTL
/w/mypage dnakcna8190anzde 1539872708 1539876308
유저에게는 /w/mypage?signed=dnakcna8190anzde 로 전달하면 된다. 아래는 랜덤스트링을 만드는 코드다.
package main

import (
	"fmt"
	"math/rand"
	"time"
)

var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")

func RandStringRunes(n int) string {
	rand.Seed(time.Now().UnixNano())
	b := make([]rune, n)
	for i := range b {
		b[i] = letterRunes[rand.Intn(len(letterRunes))]
	}
	return string(b)
}

func main() {
	fmt.Println(RandStringRunes(32))
}

HMAC를 이용

위 방식은 데이터베이스를 사용해야 한다는 불편함이 있다. TTL이 지난 레코드를 관리해줘야 하는 것도 문제다. HMAC을 이용해서 구현해 보기로 했다.
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?signed=%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/w/mypage", now)
	fmt.Println("Request url : ", macUrl)
	if MAC.check(macUrl) {
		fmt.Println("Test OK")
	} else {
		fmt.Println("Test Fail")
	}
}

		
MAC.encryptURL 메서드에 사이닝할 URL을 입력하면, 메서드는 스크릿키(Secret key)를 이용해서 URL을 사이닝한다. 이 때 현재 시간도 함께 사이닝을 하기 때문에, 해당 시간에 유일한 사이닝메시지가 만들어진다. 그리고 서버는 Secret key를 이용해서 사이닝이 올바른지를 확인 할 수 있다. 결과적으로 특정시간에 사이닝한 URL인지를 검증 할 수 있다. 위 예제는 TTL까지를 명시하지는 않았는데, 만료시간을 설정하는 정도로 간단하게 TTL 장치를 만들 수 있다.

서버는 아래와 같이 비공개 문서를 특정 유저에게 공개 할 수 있을 것이다.

 비공개 문서 제공 프로세스

다른 응용

AWS S3 에서도 signed url을 사용한다. S3를 이용해서 클라우드 스토리지서비스를 만든다고 가정해보자. 아래와 같이 다운로드&업로드 서버를 만들 수 있을 것이다.

 다운로드/업로드서버

이 방식은 아래의 문제점을 가진다. S3는 signed url기능을 이용해서 백앤드를 아래와 같이 구성할 수 있다.

 S3 signed url을 이용한 Download/Upload 서비스 구성

이방식의 장점이다.