블로그 이미지
평범하게 살고 싶은 월급쟁이 기술적인 토론 환영합니다.같이 이야기 하고 싶으시면 부담 말고 연락주세요:이메일-bwcho75골뱅이지메일 닷컴. 조대협


Archive»


 
 

REST API의 이해와 설계

아키텍쳐 /대용량 아키텍쳐 | 2016.04.05 22:33 | Posted by 조대협

REST API 이해와 설계


조대협 (http://bcho.tistory.com)


REST API에 대한 이해와 디자인 방법 그리고 보안방법에 대해서 설명합니다.




대용량 아키텍쳐와 성능 튜닝 책의 4장을 정리한 내용인데, 강의나 스터디등에 자유롭게 사용하셔도 됩니다.  (출처 명기 필요)




REST JWT(JSON Web Token)소개 - #2 node.js에서 JWT 사용하기

조대협 (http://bcho.tistory.com)


Jwt-simple을 이용한 node.js에서 JWT 토큰 사용하기

앞에서 http://bcho.tistory.com/999 JWT 토큰에 대한 개념에 대해서 간략하게 알아보았다. 그러면,실제로 JWT 토큰을 어떻게 생성하고 파싱하는 지를 node.js를 샘플을 통해서 알아보도록 하자.

시작부터 말하자면. 너무 쉽다. 쉬워도 너무..!!

node.js에는 여러가지 JWT 라이브러리가 있지만 그중에서 가장 사용이 편리한 라이브러리는 jwt-simple 이라는 라이브러리가 있다. 먼저 npm을 이용해서 해당 패키지를 설치한 후에

 


<그림. npm을 이용하여, jwt-simple 모듈을 설치>

다음과 같은 코드를 작성한다.

var jwt = require('jwt-simple');

var body= { name:'terry.cho',id:'terry',company:'coca'}

var secret ='mysecret';


var token = jwt.encode(body,secret);

console.log('token :'+token);


var decoded = jwt.decode(token,secret);

console.log('decoded:'+JSON.stringify(decoded));


위의 코드에서 body에 해당하는 부분이 JWT 토큰안에 들어갈 실제 claim이다. 그리고 secret은 HMAC-SHA1-256 을 생성하기 위한 비밀 키로, jwt.encode에 이 claim JSON과, secret을 주면, token이 생성된다.

반대로, decode에 encoding된 토큰과 secret을 인자로 넘겨주면 claim을 디코딩할 수 있다.

 


<그림. 예제 실행 결과>

디폴트로 HMAC-SHA1256 (HS256이라고 함)을 지원하며, HS384,HS512와 RS256도 지원한다.

자바,파이썬등의 JWT 라이브러리

그외에 다른 프로그래밍 언어에도 여러가지 JWT 라이브러리가 제공된다. 아래는 Atlassian 홈페이지에서 참고한 JWT 라이브러리 목록이다.

Language

Library

Java

atlassian-jwt and jsontoken

Python

pyjwt

Node.js

node-jwt-simple

Ruby

ruby-jwt

PHP

firebase php-jwt and luciferous jwt

.NET

jwt

Haskell

haskell-jwt


<그림. 다른 프로그래밍 언어의 JWT 라이브러리>

※ 출처 : https://developer.atlassian.com/static/connect/docs/concepts/understanding-jwt.html

이외에도 http://jwt.io/ 사이트에 가면, 각종 언어에 대한 JWT 라이브러리를 제공한다.


문제점

사용이 쉽고, 서버의 개발 부담을 덜어줄 수 있다는 여러가지 장점을 가지고 있으나, 그만큼 또 단점도 가지고 있다.


길이

Claim에 넣는 데이터가 많아질 수 록, JWT 토큰의 길이가 길어진다. API 호출등에 사용할 시에,매 호출마다 헤더에 붙어서 가야하기 때문에, 길이가 길다는 것은 그만큼 네트워크 대역폭 낭비가 심하다는 의미이다.


한번 발급된 토큰은 값을 수정하거나 폐기가 불가

JWT는 토큰 내에 모든 정보를 다 가지고 있기 때문에, 한번 발급된 토큰에 대한 변경은 서버에서는 더 이상 불가능하다. 예를 들어 토큰을 잘못 발행해서 삭제하고 싶더라도, Signature만 맞으면 맞는 토큰으로 인식을 하기 때문에, 서버에서는 한번 발급된 토큰의 정보를 바꾸는 일등이 불가능하다.

그래서 만약에 JWT를 쓴다면, Expire time을 꼭 명시적으로 두도록 하고, refresh token등을 이용해서, 중간중간 토큰을 재발행하도록 해야 한다. (하루 단위 정도?) 


보안

JWT는 기본적으로 Claim에 대한 정보를 암호화 하지 않는다. 단순히 BASE64로 인코딩만 하기 때문에, 중간에 패킷을 가로채거나, 기타 방법으로 토큰을 취득했으면 토큰 내부 정보를 통해서 사용자 정보가 누출 될 수 있는 가능성이 있다. 특히 자바스크립트 기반의 웹 클라이언트의 경우 브라우져상의 디버거등을 통해서 토큰이 노출될 가능성이 높다.

그래서, 이를 보완하는 방법으로는 토큰 자체를 암호화 하는 방법이 있다. JSON을 암호화 하기 위한 스펙으로는 JWE(JSON Web Encryption)


JWT(JSON Web Token)을 이용한 API 인증 - #1 개념 소개

조대협 (http://bcho.tistory.com)


REST API에 대한 보안과 인증이 화두가 되면서 많이 언급되는 것이 OAuth인데, 근래에 들어서 화두가 되고 있는 것이 JWT (JSON Web Token)이라는 표준이다.


Claim기반 토큰의 개념


OAuth에 의해서 발급되는 access_token은 random string으로 토큰 자체에는 특별한 정보를 가지고 있지 않는 일반적인 스트링 형태 이다. 아래는 페이스북에서 발급된 access_token의 형태로 일반적인 문자열 형태임을 확인할 수 있다.



<그림1.  Facebook의 Oauth에서 사용하는 일반적인 스트링 기반 토큰 예제>

 

 API나 서비스를 제공하는 서버 입장에서 그 access_token을 통해서 사용자에 연관된 권한(예를 들어 scope같은 것)을 식별한 뒤 권한을 허용해주는 구조이다.

즉 서비스를 제공하는 입장에서는 토큰을 가지고 그 토큰과 연관된 정보를 서버쪽에서 찾아야 한다. (사용자 ID나 권한등).

JWT는 Claim 기반이라는 방식을 사용하는데, Claim이라는 사용자에 대한 프로퍼티나 속성을 이야기 한다. 토큰자체가 정보를 가지고 있는 방식인데, JWT는 이 Claim을 JSON을 이용해서 정의한다.

다음은 Claim을 JSON으로 서술한 예이다.이 JSON 자체를 토큰으로 사용하는 것이 Claim 기반의 토큰 방식이다.

{

  "id":"terry"

  ,"role":["admin","user"]

  ,"company":"pepsi"

}

<코드 1. JSON으로 Claim을 기술한 토큰의 형태 >

자 그렇다면, 이러한 Claim 방식의 토큰은 무엇이 좋을까? 이 토큰을 이용해서 요청을 받는 서버나 서비스 입장에서는 이 서비스를 호출한 사용자에 대한 추가 정보는 이미 토큰안에 다 들어가 있기 때문에 다른 곳에서 가져올 필요가 없다는 것이다.

“사용자 관리” 라는 API 서비스가 있다고 가정하다.

 이 API는 “관리자(admin)” 권한을 가지고 있는 사용자만이 접근이 가능하며, “관리자” 권한을 가지고 있는 사용자는 그 관리자가 속해 있는 “회사(company)”의 사용자 정보만 관리할 수 있다. 라고 정의하자

이  시나리오에 대해서 일반적인 스트링 기반의 토큰과 JWT와 같은 Claim 기반의 토큰이 어떤 차이를 가질 수 있는 지 알아보도록 하자.


OAuth 토큰의 경우



<그림 2. String 토큰에 의한 API 권한 인증 흐름>

 

1.    API 클라이언트가 Authorization Server (토큰 발급서버)로 토큰을 요청한다.

이때, 토큰 발급을 요청하는 사용자의 계정과 비밀번호를 넘기고, 이와 함께 토큰의 권한(용도)을 요청한다. 여기서는 일반 사용자 권한(enduser)과 관리자 권한(admin)을 같이 요청하였다.

2.    토큰 생성 요청을 받은 Authorization Server는 사용자 계정을 확인한 후, 이 사용자에게 요청된 권한을 부여해도 되는지 계정 시스템등에 물어본 후, 사용자에게 해당 토큰을 발급이 가능하면 토큰을 발급하고, 토큰에 대한 정보를 내부(토큰 저장소)에 저장해놓는다.

3.    이렇게 생성된 토큰은 API 클라이언트로 저장된다.

4.    API 클라이언트는 API를 호출할때 이 토큰을 이용해서 Resource Server(API 서버)에 있는 API를 호출한다.

5.    이때 호출되는 API는 관리자 권한을 가지고 있어야 사용할 수 있기 때문에, Resource Server가 토큰 저장소에서 토큰에 관련된 사용자 계정, 권한 등의 정보를 가지고 온다. 이 토큰에 (관리자)admin 권한이 부여되어 있기 때문에, API 호출을 허용한다. 위에 정의한 시나리오에서는 그 사용자가 속한 “회사”의 사용자 정보만 조회할 수 있다. 라는 전제 조건을 가지고 있기 때문에, API 서버는 추가로 사용자 데이타 베이스에서 이 사용자가 속한 “회사” 정보를 찾아와야한다.

6.    API서버는 응답을 보낸다.


JWT와 같은 Claim 기반의 토큰 흐름을 보자

 



<그림 3. Claim 기반의 토큰을 이용한 API 권한 인증 흐름 >

 

1.    토큰을 생성 요청하는 방식은 동일하다.  마찬가지로 사용자를 인증한다음에, 토큰을 생성한다.

2.    다른 점은 생성된 토큰에 관련된 정보를 별도로 저장하지 않는다는 것이다. 토큰에 연관되는 사용자 정보나 권한등을 토큰 자체에 넣어서 저장한다.

3.    API를 호출하는 방식도 동일하다.

4.    Resource Server (API 서버)는 토큰 내에 들어 있는 사용자 정보를 가지고 권한 인가 처리를 하고 결과를 리턴한다.

결과적으로 차이점은 토큰을 생성하는 단계에서는 생성된 토큰을 별도로 서버에서 유지할 필요가 없으며

토큰을 사용하는 API 서버 입장에서는 API 요청을 검증하기 위해서 토큰을 가지고 사용자 정보를 별도로 계정 시스템 등에서 조회할 필요가 없다는 것이다.


참고 : 다른 Claim 기반 토큰은?


그러면 이러한 Claim 기반의 토큰이 JSON이 처음일까? 이미 이전에, XML기반의 SAML 2.0이 이와 비슷한 개념을 가지고 있다. Assertion이라는 개념으로 XML안에 이러한 Claim 정보를 넣어서 넘길 수 있었으나, 문제점은 전체적인 사이즈가 너무 크고, 구조가 복잡하여 쉽게 접근이 어려웠다. 더군다가 크기가 크기 때문에 API와 같이 자주 호출해야 하는 경우에는 HTTP 헤더등에 실어서 보내기가 어렵고, 파싱에 대한 오버해드가 크기 때문에 적절하지 않았다. (주로 다른 사이트나 시스템간의 SSO에서 상호 사용자 인증등을 위해서 사용된다. 무겁기는 하지만 표준화가 잘되어 있기 때문에 사용자 인증 시나리오에서는 현재에도 많이 사용된다.)

JWT는 이JSON Claim을 BASE64로 인코딩하여HTTP Header에 쉽게 넣을 수 있으며, JSON 기반이기 때문에 파싱과 사용이 쉽다.

결과적으로 Claim 기반의 토큰은 토큰 자체가 정보를 담음으로써, 토큰을 가지고 서비스나 API 접근을 제어할 때 별도의 작업이 서버에서 필요하지 않으며, 토큰 자체를 서버에서 관리할 필요가 없기 때문에 구현이 상대적으로 단순해진다.


JWT에 대한 소개


Claim 기반의 토큰에 대한 개념을 대략적으로 이해했다면, 그러면 실제로 JWT가 어떻게 구성되는지에 대해서 살펴보도록 하자.


Claim (메세지) 정의

JWT는 Claim을 JSON형태로 표현하는 것인데, JSON은 “\n”등 개행문자가 있기 때문에, REST API 호출시 HTTP Header등에 넣기가 매우 불편하다. 그래서, JWT에서는 이 Claim JSON 문자열을 BASE64 인코딩을 통해서 하나의 문자열로 변환한다.

{

  "id":"terry"

  ,"role":["admin","user"]

  ,"company":"pepsi"

}

<코드 2. JSON 기반의Claim 예제>

문자열을 BASE64 인코딩 한 결과

ew0KICAiaWQiOiJ0ZXJyeSINCiAgLCJyb2xlIjpbImFkbWluIiwidXNlciJdDQogICwiY29tcGFueSI6InBlcHNpIg0KfQ0K

<코드 3. JSON 기반의 Claim 코드 2를 BASE64 인코딩 한 결과>


변조 방지

위의 Claim 기반의 토큰을 봤으면, 첫번째 들 수 있는 의문이 토큰을 받은 다음에 누군가 토큰을 변조해서 사용한다면 어떻게 막느냐? 이다. 이렇게 메세지가 변조 되지 않았음을 증명하는 것을 무결성(Integrity)라고 하는데, 무결성을 보장하는 방법중 많이 사용되는 방법이 서명(Signature)이나 HMAC 사용하는 방식이다. 즉 원본 메세지에서 해쉬값을 추출한 후, 이를 비밀 키를 이용해서 복호화 시켜서 토큰의 뒤에 붙인다. 이게 HMAC방식인데,  누군가 이 메세지를 변조를 했다면,변조된 메세지에서 생성한 해쉬값과 토큰뒤에 붙어 있는 HMAC값이 다르기 때문에 메세지가 변조되었음을 알 수 있다. 다른 누군가가 메세지를 변조한후에, 새롭게 HMAC값을 만들어내려고 하더라도, HAMC은 앞의 비밀키를 이용해서 복호화 되었기 때문에, 이 비밀키를 알 수 없는 이상 HMAC을 만들어 낼 수 없다.


※ HMAC에 대한 자세한 설명은http://bcho.tistory.com/807 를 참고하기 바란다.

그래서 앞의 JSON 메세지에 대해서 SHA-256이라는 알고리즘을 이용해서 비밀키를 “secret” 이라고 하고, HMAC을 생성하면 결과는 다음과 같다.

i22mRxfSB5gt0rLbtrogxbKj5aZmpYh7lA82HO1Di0E

<코드 4. 코드 2의 JSON기반 Claim에 대해서, SHA1-256으로 생성한 HMAC>

서명 생성 방식

그러면 무결성 보장을 위해서 사용할 수 있는 알고리즘이 SHA1-256 HMAC 뿐일까? 보안요건에 따라서 SHA1-256,384,512. 그리고 공인 인증서 (Ceritification)을 이용한 RS256 등등 다양한 서명 방식을 지원한다. 그렇다면 JWT 토큰이 어떤 방식으로 서명이 되어 있는지는 어떻게 알 수 있을까?

그래서 JWT 토큰의 맨 앞부분에는 서명에 어떤 알고리즘을 사용했는지를 JSON형태로 정의한후, 이 JSON을 다시 BASE64 방식으로 인코딩한 문자열을 붙인다

{"alg":"HS256","typ":"JWT"}

<코드 5. JSON으로 서명 생성 방식은 SHA1-256으로 정의한 예>

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

<코드 6. 위의 코드 5 JSON 문자열을 BASE64 인코딩한 결과>

 

전체 메세지 포맷


위에서 설명한, 서명 방식, JSON 기반의 Claim,그리고 서명(Signature)까지 포함된 전체적인 JWT 토큰의 구조를 보면 다음과 같다.

{서명 방식을 정의한 JSON을 BASE64 인코딩}.{JSON Claim을 BASE64 인코딩}.{JSON Claim에 대한 서명}

이를 정리해서 그림으로 서술해 보면 다음과 같다.


<그림. JWT 토큰 구조>

그리고 결과로 나온, JWT 토큰은

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.ew0KICAiaWQiOiJ0ZXJyeSINCiAgLCJyb2xlIjpbImFkbWluIiwidXNlciJdDQogICwiY29tcGFueSI6InBlcHNpIg0KfQ0K.i22mRxfSB5gt0rLbtrogxbKj5aZmpYh7lA82HO1Di0E

가 된다.


2편에서 다룰 내용

  • 유출 방지(암호화)

  • 상세 스펙

  • 구현예제