JWT 란? (JWT vs Session)
참조 : https://www.bezkoder.com/jwt-json-web-token/
1. Authentication 이란?
어떠한 웹사이트를 사용할 때, 계정을 생성하고, 어떠한 특정한 기능을 사용하기위해 로그인하여 접근해야한다. 그러한 action 을 우리는 Authentication 이라고 부른다.
2. Session 기반의 Authentication
그렇다면, 어떻게 계정을 인증(authenticate) 할까?
우선, 과거에 가장 많이 사용한 심플한 방법인 Session-based Authentication 을 살펴보자!!
위의 사진을 보면서 설명해보자.
1. 사용자가 website에 로그인
2. Server는 그 사용자에 대한 Session을 생성하여 저장 (in Memory or Database)
3. Server는 브라우저 Cookie 에 저장할 그 Client의 SessionId를 return
Server에 있는 그 Session은 만료시간을 가지고 있음. 그 일정 시간이 지난 후, 그 Session은 만료되고 다른 Session을 생성하여 다시 로그인을 해야함
4. 로그인이 된 사Cookie 와 SessionId 의 HTTP Request는 Server로 감
5. Server는 인증을 위해 이 SessionId 와 저장된 Session을 비교한 후에
6. 적절한 Response를 return 함
3. Token-based Authentication이 탄생한 이유?
그런데, 우리는 왜 Token-based Authentication 을 많이 사용하는 걸까?
이유는, 우리는 website만 가지고 있는 것이 아니고, 많은 플랫폼들이 존재하기 때문이다!
가정해보자, 우리가 사용하는 website가 Session으로 잘 작동한다고 하자. 어느날, 우리가 Mobile (Navtive Apps) 에 시스템을 구축해야하고 같은 Database를 최신 Web app에 사용해야한다면 우리는 어떻게 해야할까?
우리는 Native App의 사용자를 Session을 기반으로 하는 Authentication로 인증할 수 없다. 왜냐면, Native App 과 비슷한 아이들은 Cookie가 없기 때문이다!!!!
그러면, 우리는 Native App 을 위한 Backend 프로젝트를 다시 빌드 하거나, Native App을 위한 Authentication module 을 새로 구축해야한다. 그래서 탄생 한 것이 Token-based Authentication!!!
이 방법으로, 사용자의 login 상태가 Server에 의해 JWT(JSON Web Token) 으로 encoding 되어져서 Client로 보내질 수 있다. 그래서 요즘 RESTful API들은 이것을 많이 사용한다. 그러니 이제 JWT 의 원리에 대해서 알아보자!!!
4. Token-based Authentication의 원리
아래의 사진과 같이 이해하기 간단하다.
JWT의 작동
Session을 생성하는 것 대신에, Server는 로그인한 사용자의 data로부터 JWT를 생성하고 Client 로 보낸다. 그 Client 는 생성된 JWT를 이제부터 저장하고 되고, 그 Client로 부터 오는 모든 Request는 모두 JWT를 (보통은 header에) 붙여서 온다. 그러면 Server는 JWT를 인증하고 Response를 return 해준다.
Browser : Local Storage
IOS : Keychain
Android : SharedPreferences
JWT의 생성 방법
JWT 의 중요한 3가지
Header
Payload
Signature
Header
Header는 질문에 대답한다.
우리가 어떻게 JWT를 계산할까?
이제, header의 예제를 살펴보자. JSON 객체는 이렇게 생겼다:
{ "typ" : "JWT", "alg": "HS256" }
typ - 'type', Token의 종류를 나타낸다. 여기서는 of course JWT
alg - 'algorithm', Token signature 를 생성하기 위한 hash 알고리즘. 알고리즘은 Secret Key를 사용 여기서는, HMAC-SHA256 을 말한다.
Payload
Payload는 우리가 대답할 수 있게 도와준다.
우리는 JWT에 무엇을 저장하고 싶지?
이제, payload는 이렇게 생겼다.
{
"userId": "abcd1234",
"username": "minah",
"email": "contact@minah.com",
// standard fields
"iss": "park, author of minah.com",
"iat": 4568874212,
"exp": 4568874212
}
3가지의 저장하는 사용자 항목들 : userId , username, email
Standard Fields (선택):
iss (Issuer) - JWT 발행한사람
iat (Issued at) - JWT가 발행된 시간
exp (Expiration Time) - JWT의 만료 시간
더 많은 저장할 수 있는 항목들: https://en.wikipedia.org/wiki/JSON_Web_Token#Standard_fields
Signature
위에 정의한 Hash Algorithm 을 사용하는 부분!
const data = Base64UrlEncode(header) + '.' + Base64UrlEncode(payload);
const hashedData = Hash(data, secret);
const signature = Base64UrlEncode(hashedData);
1) Header 와 Payload 를 encode 하고, 그것들을 마침표 ( . ) 로 합친다.
data = '[encdoeHeader].[encodedPayload]'
2) 이제, Header에서 정의한 Hash algorithm 과 secret 문자를 활용해서 데이터의 hash 를 만든다.
3) hash한 결과물은 Signature을 얻기위해 encodeing 한다.
Header, Payload, Signature 을 모두 합쳐
JWT standard structure 에 Header, Payload, Signature을 모두 합침
const encodedHeader = base64urlEncode(header);
/* Result */
"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9"
const encodedPayload = base64urlEncode(payload);
/* Result */
"eyJ1c2VySWQiOiJhYmNkMTIzNDVnaGlqayIsInVzZXJuYW1lIjoiYmV6a29kZXIiLCJlbWFpbCI6ImNvbnRhY3RAYmV6a29kZXIuY29tIn0"
const data = encodedHeader + "." + encodedPayload;
const hashedData = Hash(data, secret);
const signature = base64urlEncode(hashedData);
/* Result */
"crrCKWNGay10ZYbzNG3e0hfLKbL7ktolT7GqjUMwi3k"
// header.payload.signature
const JWT = encodedHeader + "." + encodedPayload + "." + signature;
/* Result */
"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiJhYmNkMTIzNDVnaGlqayIsInVzZXJuYW1lIjoiYmV6a29kZXIiLCJlbWFpbCI6ImNvbnRhY3RAYmV6a29kZXIuY29tIn0.5IN4qmZTS3LEaXCisfJQhrSyhSPXEgM1ux-qXsGKacQ"
5. JWT는 데이터를 secure 하는 방법
JWT 는 데이터를 암호화하여 보호하거나, 감추지 않는다.
단지, JWT를 생성하는 과정이 데이터를 encode하고 hash 할 뿐 암호화 하지 않는다. 그래서, JWT를 Main-in-the-middle attack으로 훔칠 수도있다. 그렇기 때문에 application 은 HTTPS encryption (암호화) 를 가지고 있어야한다.
6. Server가 Client 로 부터 JWT를 확인하는 방법
앞전에, Signature를 생성하는데 Secret 문자를 사용했다. 그 Secret 문자는 모든 application마다 유니크하고 안전한 server side에 보관되어 있어야한다.
Client로 부터 JWT를 받았을 때, Server는 Signature를 받아 그 Signature가 같은 알고리즘과 Secret문자로 hash 되었는지 확인한다.
중요!!
Server로 Payload를 보낼 때, Payload정보는 숙력된 프로그래머들에 의해서 편집이나 추가가 될 수 있다.
그러면 어째야하냐구?
Token 을 Client로 보내기전에 저장해야한다. 그럼으로써, 나중에 Client로 부터 보내진 JWT가 valid 하다는걸 보장할 수 있다.
또한, 사용자의 Token을 Server에 저장하는 것 또한 시스템으로부터 Force Logout 기능도 도울 것이다.
참고 사이트를 내 나름대로 번역해서 적은 것이기 때문에, 영어에 능한 사람은 맨위에 있는 참고사이트를 보는 것을 더욱 추천한다! 역시 영어가 더욱 직설적이고 심플하게 표현되다보니 이해하는게 더 쉽다.