1. 사용자 인증의 여러 방식들
1) 쿠키와 세션
HTTP는 Stateless , Connectionless 특징이 있기 때문에, 사용자 인증을 유지할 수 있는 기능이 필요했습니다.
그래서 클라이언트가 로그인 등으로 한 번 사용자 인증을 하면, 그 인증을 유지할 수 있도록 쿠키와 세션이라는 기술을 지금껏 사용해 왔습니다.
그러나 쿠키는 브라우저에 사용자 정보가 기록되기 때문에 위변조의 가능성이 높아 보안에 취약하며,
세션 또한 서버의 메모리를 차지하고 있기 때문에 동접자가 많은 웹 사이트일 경우 서버 과부화의 원인이 되며, 또한 세션 정보가 중간에 탈취 당할 수 있기 때문에 보안에 완벽하다고 할 수가 없습니다.
쿠키와 세션의 문제점들을 보완하기 위해 토큰( Token )기반의 인증 방식이 도입되었습니다.
토큰 기반의 인증 방식의 핵심은 보호할 데이터를 토큰으로 치환하여 원본 데이터 대신 토큰을 사용하는 기술입니다.
만약 토큰 전송 과정에서 중간에 토큰이 탈취 당하더라도 데이터에 대한 정보가 암호화 되어있어 공격자가 본래 내용을 알 수 없게 됩니다.
즉, 보안적으로 우수하다고 할 수 있습니다.
2) OAuth
토큰 방식을 사용하는 웹 서비스는 우리가 여러 웹 서비스를 이용하면서 쉽게 찾아볼 수 있습니다.
"페이스북으로 로그인 하기", "구글 계정으로 로그인하기" 처럼 다른 웹 서비스의 계정으로 사용자 인증을 대신하는 기술을 OAuth라고 합니다.
( OAuth에 대한 자세한 내용은 여기를 참고해주세요 ! )
OAuth는 토큰 기반의 사용자 인증 방식입니다.
구글, 페이스북으로부터 토큰을 발급 받아 구글, 페이스북에 로그인 할 수 있도록 권한을 부여 받는 것이죠.
여기서 권한이란 로그인 뿐만 아니라 API를 호출하는 권한도 의미합니다.
그런데 여기서 발급 받은 토큰은 아무 의미 없는 문자열 형태입니다.
때문에 구글, 페이스북 서버에서는 토큰과 연관된 정보를 탐색 한 후에 어떤 기능을 수행하게 됩니다.
이와 달리, 이 글에서 살펴 볼 JWT는 서버의 탐색이 필요 없습니다.
이제 JWT에 대해 알아보도록 하겠습니다.
2. JWT ( JSON Web Token )
(JWT에 대한 공식 문서는 여기와 여기를참고해주세요.)
JWT는 이름에서 알 수 있듯이 Json으로 된 Token을 사용합니다.
즉, OAuth와 같이 Token 기반의 인증 방식이라는 것입니다.
그런데 JWT는 토큰 자체가 의미를 갖는 Claim 기반의 토큰 방식입니다.
Claim( 권한 )은 사용자에 대한 프로퍼티나 속성을 의미합니다.
즉, JWT는 OAuth처럼 아무 의미없는 문자열로 된 토큰이 아니라는 것이죠.
Token을 생성하고 요청하는 프로세스는 다음과 같습니다.
- JSON 객체에 요구사항을 작성
- 어떠한 암호화 방식을 사용해서 문자열로 인코딩
- HTTP header에 추가함으로써 사용자 인증을 요청
- 서버에서는 Header에 추가된 Token을 디코딩하여 사용자를 인증합니다.
JWT 특징
JWT의 가장 큰 특징은
- 정보가 담긴 데이터( JSON 객체 )를 암호화 하여, HTTP 헤더에 추가 시킨다는 것입니다.
- 보안성 증가
- 권한을 부여하기 위해 필요한 데이터가 JWT안에 모두 담겨있습니다.
- OAuth 처럼 인증 서버에서 토큰에 대한 정보를 찾을 필요가 없습니다.
그렇다고 JWT가 보안에 완벽한 것은 아닙니다.
누군가가 토큰을 탈취한다면, 그 토큰을 이용하여 권한을 수행할수 있습니다.
그래서 토큰을 서버에 저장하는 것이 아니기 때문에 토큰에 유효시간을 설정해야 하며, 탈취 될 가능성을 줄이기 위해 유효시간을 짧게 해주는 것이 좋습니다.
JWT의 데이터 무결성 - HMAC
JWT는 토큰이 탈취 당하더라도 위변조의 위험을 벗어날 수 있도록 무결성을 보장하는 몇 가지 방법이 있습니다.
그 방법 중 하나는 데이터를 암호화 하고, 해싱하는 HMAC(Hash-based Message Authentication) 기법을 사용하는 것입니다.
해싱은 원문을 일정 길이의 byte로 변환하는데 그 결과가 유일하다는 특징이 있습니다.
즉, 원문이 조금이라도 바뀌면 해싱의 결과는 완전히 달라집니다.
그래서 토큰을 탈취해서 데이터를 수정하게 되면 해싱의 결과가 완전히 달라지므로 토큰이 위조 되었다는 것을 알 수 있게 됩니다.
지금까지 JWT가 도입된 배경과 특징들을 살펴보았는데요, 특징을 정리하면 다음과 같습니다.
- JWT는 그 자체가 암호화된 문자열 데이터입니다.
- 토큰은 HTTP header에 추가되기 때문에 서버에 따로 보관할 필요가 없으므로 서버에 부화를 일으키지 않습니다.
- 토큰을 생성할 때 암호화 과정을 거치므로 보안적으로 안전합니다.
- 그래도 짧은 시간의 유효시간을 두는 것을 권장합니다.
3. JWT 구조 및 생성
이제 JWT가 어떤 구조로 구성되어 있는지 알아보도록 하겠습니다.
JWT 토큰은 위와 같이 구성되어 있습니다.
많은 프로그래밍 언어에서 JWT를 지원하는데, 각 언어의 라이브러리에서 자동으로 인코딩 및 해싱작업을 해줍니다.
단, 헤더, 내용, 서명에 적절한 속성과 값들을 명시할 때 이야기입니다.
위 사진은 JWT 공식홈페이지에서 제공하는 디버거 기능입니다.
이를 기반으로 JWT 구조를 살펴보도록 하겠습니다.
1) 헤더 ( header )
헤더에는 typ와 alg 속성을 명시합니다.
- typ
- 토큰의 타입을 명시합니다.
- alg
- 해싱 알고리즘을 명시합니다.
- 이 알고리즘은 서버에서 토큰을 검증할 때 signature에서 사용됩니다.
2) 내용 ( payload )
내용에는 토큰에 대한 정보를 작성합니다.
정보는 속성, 값으로 표현되며 이를 claim이라 합니다.
claim은 다음과 같이 3가지로 작성할 수 있습니다.
- registered claim
- 미리 정의된 claim으로써, 토큰에 대한 정보를 작성합니다.
- iss
- 토큰 발급자
- exp
- 토큰의 만료시간
- sub
- 토큰 제목
- aud
- 토큰 대상자
- 그 밖에 여러 claim들이 있으며, 더 많은 정보는 JWT 공식문서를 참고해주세요.
- public claim
- 공개적인 claim을 명시하는데, 충돌방지를 위해 URI 형식으로 작성합니다
- private claim
- 서버와 클라이언트가 협의한 claim을 명시합니다.
3) 서명 ( signature )
서명에서는 헤더(header)의 인코딩 값과 내용(payload)의 인코딩 값을 "."으로 연결하여 합친 후 비밀키로 해싱합니다.
일종의 암호화하는 작업이라고 생각하시면 됩니다.
위의 header, payload, signature의 각 값들을 "."으로 합치면 하나의 JWT가 생성됩니다.
이렇게 생성된 JWT를 HTTP header에 추가하여 서버에 요청을 하면 서버에서는 이를 디코딩하여 사용자 인증을 하게 됩니다.
JWT는 자체적으로 정보를 갖고 있는 토큰이기 때문에 서버에 저장될 필요가 없습니다.
즉, 서버로부터 독립적이라 할 수 있으며, 서버의 부담을 덜어줄 수 있다는 장점이 있습니다.
그렇다면 JWT는 어디에 저장하는 것이 좋을까요?
브라우저의 쿠키, local storage, session storage에 저장할 수 있지만, HTTP Only 옵션의 쿠키에 저장하는 것이 좋습니다.
이상으로 JWT에 대해 알아보았습니다.
JWT가 좋다는 것은 알겠는데, 어떤 상황에서 써야 할지는 잘 모르겠습니다.
세션, OAuth, JWT 모두 각각의 특징이 있기 때문에 어떤 서비스에 적절한 방식인지는 좀 더 공부가 필요할 것 같습니다.
[ 참고 자료 ]
https://velopert.com/2389
http://bcho.tistory.com/999#recentComments