시작하며
대외활동에서 프로젝트를 하면서 보안적인 측면의 안전과 서버의 부담을 줄이기 위해서 JSON Web Token을 이용하여 사용자 인증과정을 거쳤는데, 어떻게 하면 된다는 방식은 알고있었지만 자세히 정리하여 내 것으로 만든 적이 없었기 때문에 정리를 해보려고 한다.
들어가며
JWT(JSON Web Token)기반의 회원인증 시스템 구현에 대해서 설명을 시작하기에 앞서 JWT를 포함한 총 3가지의 사용자 인증방식에 대해 알아보자.
- Cookie
- Session
- JWT
-쿠키(Cookie)란
하이퍼 텍스트의 기록서(HTTP)의 일종으로서 사용자가 어떠한 웹사이트를 방문할 경우 그 사이트가 사용하고 있는 서버를통해 인터넷 사용자의 컴퓨터에 설치되는 작은 기록 정보 파일을 일컫는다. 이 기록 파일에 담긴 정보는 인터넷 사용자가 같은 웹사이트를 방문할 때마다 읽히고 수시로 새로운 정보로 바뀐다.
[출처: 위키백과]
<Firefox 설정 -> 개인정보 및 보안 -> 데이타 관리에 들어갔을 때>
- 쿠키의 단점
쿠키라는 용어는 IT쪽에 직접적인 관련이 없는 일반인들도 상당히 많이 들어봤을 법한 말이라고 생각하는데, 이는 요즘 스마트폰에서 용량을 확보하기 위해 '쿠키를 삭제하시겠습니까?' 라는 용어나 FIrefox나 Explorer에서도 사용자가 직접 쿠키를 삭제하는 방법도 꽤나 잘 알려져 있기 때문이라고 생각한다. 이렇듯 쿠키는 자주 사용되고 변경되는만큼 사용자 뿐만 아니라 악성 유저에게도 탈취될 가능성을 무시할 수 없는데, 혹시 보안이 허술한 웹사이트에 들어가게된다면 사용자의 개인정보에 관련된 쿠키가 쌓이게 되고 클라이언트와 서버가 쿠키를 주고 받는 과정에서 탈취되어 이는 악성유저에게 악용될 여지를 만들어낼 수 있다.
- 쿠키 사용의 예
1. 방문 사이트에서 로그인을 했을 때, 아이디와 비밀번호를 저장하겠습니까?
2. 팝업창이 떴을 때, 오늘 하루/일주일간 팝업창을 보지 않음.
-세션(Session)이란
간단히 말하자면 해당 서버(웹)가 접근한 클라이언트(사용자)를 식별한 상태를 유지하는 방법이라고 할 수 있다. 즉, 사용자가 브라우저를 열어 서버에 접속한 후부터 접속을 종료한 시점까지를 말한다. Session은 설명보다는 동작원리를 보는 편이 이해가 더 편하다.
-세션의 동작원리
1. 유저(사용자)가 웹사이트에 접속하여 Request를 보낸다.
2. 해당 웹사이트(서버)가 유저가 Session ID라는 문자열을 발급받았는지 여부를 검사하고, 발급 받지않은 경우 Memory Cookie형태로 Session ID라는 임의의 긴 문자열을 가진 쿠키(JSESSIONID)를 발급해준다.(Memory Cookie는 세션 종료 시 같이 소멸되는 휘발성을 가진 쿠키이다.)
3. *HTTP 프로토콜은 비접속형 프로토콜로 매 접속시마다 새로운 연결을 필요로하는데 세션이 이 연결을 유지하도록 도와준다.
<Firefox 사용 시 F12키를 누른 후 저장소를 가서 세션을 확인가능>
*HTTP 프로토콜의 두 가지 특징 및 장점, 단점
<특징>
1. Connectionless: 클라가 서버에게 Request를 보내고 서버가 클라에게 Response를 보내고 나면 연결을 끊는다.
2. Stateless: 접속을 끊는 순간 상태정보를 유지하지않는다.
<장점 및 단점>
위의 두 가지의 특징으로 인해 자원을 계속해서 낭비할 필요가 없어진 것이 장점이라고 할 수 있고, 단점은 상태를 유지하지않기때문에 매번
통신할때마다 클라이언트의 신원을 체크해야한다.
-쿠키와 세션의 간단한 같은 점과 차이점
쿠키와 세션은 어떻게 보면 둘 다 쿠키를 쓴다는 점과 상태를 유지시켜준다는 점에서 비슷하다고 할 수 있습니다.
하지만 이 둘의 가장 큰 차이점은 저장되는 장소의 차이라고 볼 수있고, 이로 인해 보안성과 속도의 차이가 나타날 수 있다.
쿠키는 위에서 말했듯이 클라이언트에게 저장되기 때문에 서버요청시 처리 속도는 빠르지만 탈취 및 악용의 가능성을 이유로 개인의 신상정보나 로그인 정보등이 중요한 정보가 들어있어서는 안된다.
이와 다르게 세션은 클라이언트에게 SessionID를 주고 서버 또한 SessionID를 가지고 있으므로 서버의 자원을 사용하게 되는데 세션은 쿠키 서버의 응답을 기다려야하는 만큼 처리속도도 느리고 이는 사용자 수가 많아졌을 경우 서버에게 부담을 줄 수 있다.
-JWT란?
JSON Web Token의 줄임말로써, 인증정보를 암호화하여 전달하기때문에 쿠키에 비해 안전하고 서버의 자원을 쓰는 일도 없는 위의 두 가지 방법의 단점을 보완한 토큰을 이용한 인증방식이다.
-JWT의 특징
1. 우선, JWT시스템에서 발급된 토큰은 토큰의 기본정보(typ, alg), 전달할 정보(로그인시 필요한 사용자의 정보) 그리고 토큰의 검증을 증명해줄 Signature(서명)까지 가지고 있어 자가수용적(Self-Contained)이라 할 수 있습니다.
2. JSON 포맷으로 통신을 하여 C, Java, Python, C++, R, C#, PHP, JavaScript, Ruby, Go, Swift 등 수많은 프로그래밍 언어에서 지원됩니다.
3. HTTP 헤더에 담아서 전달 혹은 URL의 파라미터로도 전달 가능하여 서버<->클라이언트간의 전달이 쉽습니다.
-JWT의 구조
우선, JWT의 구조는 http://jwt.io에 접속하기만 해도 간단하게 알 수 있는데, 다음의 사진과 같다.
좌측의 Encoded는 우측의 HEADER.PAYLOAD.SIGNATURE 순으로 각각 암호화 하여 '.(콤마)'를 이용하여 나눈것이라고 볼 수 있다.
그럼 이제 각 부분별로 어떤 특징을 가지고 있는지에 대해 자세히 알아보자.
1. HEADER
-헤더의 구성요소
헤더에는 alg, typ 이 두 가지 키가 존재하는데,
1. alg는 해싱 알고리즘을 뜻하며, Signature부분에서 검증을 위해 사용되는 해싱 알고리즘이다.
2. typ는 토큰의 타입이 무엇인지를 나타낸다.
위의 사진의 HEADER와 비교해서 설명하자면 HS 256 해싱 알고리즘을 이용한 JWT타입의 토큰을 의미한다고 볼 수 있다.
그리고 하단의 VERIFY SIGNATURE에서 볼 수 있듯이 HMACSHA256 해싱 알고리즘을 이용하여 이정보를 base64로 인코딩하면
Encoded 부분의 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 부분이 완성이 된다.
2. PAYLOAD
-PAYLOAD의 구성요소
PAYLOAD 혹은 Claim Set이라고도 하는데 이 부분에는 토큰에 담을 정보(Claim들의 Set)가 들어있다.
-이미 등록 되어있는 클레임들을 의미하며 선택적으로 사용할 수 있다.
1)iss : 토큰의 발급자를 의미(issuer)
2)sub : 토큰의 제목을 의미(subject)
3)aud : 토큰의 대상자를 의미(audience)
4)exp : 토큰의 만료시간을 의미하며 Numeric Date형식으로 되어있다.(expiration)
5)nbf : 토큰의 활성시간을 의미하며 Numeric Date형식으로 되어 이 날짜 이후부터 토큰이 활성화된다.(not before)
6)iat : 토큰의 발급시간 즉, 토큰이 발급된지 얼마나 지났는지 등 토큰의 나이를 의미(issued at)
7)jti : 토큰의 고유 식별자를 의미하며 일회용 토큰으로 사용시에 유용하다.(jwt identity)
-충돌을 방지하기 위해 공개된 클레임이며, 보통 URI형식으로 짓는다. ex) "https://publicclaim.com/jwt_claims/is_admin" : true
-공개 클레임이나 등록된 클레임과 달리 충돌의 위험이 있으니 서버와 클라이언트간에 잘 협약하여 사용하여야하며 key : value형태로 되어있다.
{
ex) "mytistory" : "ohmycode"
}
위의 클레임 들을 key : value 형태로 넣어주면 상단 jwt.io의 사진의 VERIFY SIGNATURE처럼 토큰의 정보에 해당하는 PAYLOAD에 base64방식으로 암Encoding된다.
3. SIGNATURE
서명을 해주는 중요한 부분인데, 이 부분은 위의 두 가지처럼 약속이 되어있거나 특정한 key, value값이 존재하는 것이 아닌 그야말로 토큰의 서명을 위해서 존재하는 부분이다. 앞서 생성한 HEADER와 PAYLOAD를 '.(콤마)'를 구분자로 두고 (HEADER.PAYLOAD형식) HS 256해싱 알고리즘으로 해싱을 하고 사용자가 직접 지정한 secretkey를 써서 base64방식으로 Encoding해주면 토큰의 서명 부분까지 완성된다.
이렇게 해서 JWT 하나에 대한 HEADER(알고리즘 형식, 토큰의 타입), PAYLOAD(클레임 별 토큰의 정보), SIGNATURE(앞의 두 가지를 이용한 Secretkey를 포함한 서명)까지 완성하게 된다!
끝마치며
처음에는 "전에 했던 앱에서 JWT를 사용한 회원의 인증방식에 대해서 간단히 쓰자"라고생각했지만 성격이 성격인지라; 꼼꼼하게 정리하지않으면 직성이 안풀리기 때문에 하나하나 다시 공부해가면서 적었고, 그로인해 시간이 많이 걸렸다... 다음 포스팅에는 본격적으로 JWT를 기반으로 Android에 적용한 부분까지 해보도록 하자.