서론
제작하고 있는 웹 서버에 JWT를 적용하기 위해 공부하던 중 문뜩 다른 인증 방식과 무슨 차이가 있을지 궁금하였습니다.
그래서 이 글에서는 세션/쿠키/토큰에 대해 알아보려고 합니다.
목차
1. HTTP의 특징
2. 쿠키 (Cookie)
3. 세션 (Session)
4. 쿠키와 세션의 차이
5. 쿠키와 세션의 보안상 문제
6. 토큰 (Token) & JWT (JSON Web Token)
1. HTTP의 특징
HTTP프로토콜 환경의 특징은 connectionless | stateless 으로 볼 수 있습니다.
때문에, 서버는 클라이언트가 누구인지 연결될 때 마다 매번 확인을 해야합니다.
이러한 특성은 보완하고자 쿠키와 세션 서버인증 방식이 사용됩니다.
다만, 서버과부하 및 보안 문제로 요즘은 토큰 기반의 인증방식을 사용하고 있습니다.
2. 쿠키 (Cookie)
쿠키?
- 쿠키는 클라이언트 로컬에 저장되는 키 / 값이 포함된 데이터 파일입니다.
- 인증 유효시간을 명시할 수 있으며, 유효시간 동안은 브라우저가 종료되어도 인증이 유지됩니다.
- 쿠키는 클라이언트의 상태 정보를 로컬에 저장하고, 저장한 내용을 참조합니다.
- 클라이언트에 300개까지 쿠키 저장이 가능하며, 도메인당 20개의 값을 가질 수 있습니다. 하나의 쿠키값은 4KB까지 저장할 수 있습니다.
- Response Header에 Set-Cookie 속성을 사용하면 클라이언트에 쿠키를 만들 수 있습니다.
- 쿠키는 따로 요청하지 않아도 브라우저가 Request시에 Request Header를 넣어서 자동으로 서버에 전송합니다.
구성 요소
- 이름 : 쿠키를 구별하는 데 사용되는 이름
- 값 : 쿠키의 이름과 관련된 값
- 경로 : 쿠키를 전송할 요청 경로
- 유효시간 : 쿠키의 유지시간
- 도메인 : 쿠키를 전송할 도메인
동작 방식
- 클라이언트가 페이지 요청
- 서버에서 쿠키를 생성
- HTTP 헤더에 쿠키를 포함시켜 응답
- 브라우저가 종료되어도 쿠키 유효시간동안 클라이언트에서 보관
- 같은 요청 시 HTTP 헤더에 쿠키를 함께 전송
- 이전 상태 정보를 변경 할 필요가 있다면, 쿠키를 업데이트 하여 변경된 쿠키를 HTTP 헤더에 포함시켜 응답
3. 세션 (Session)
세션?
- 세션은 쿠키를 기반하고 있지만, 사용자 정보를 브라우저에 저장하는 쿠키와 달리 세션은 서버 측에서 관리합니다.
- 서버에서 클라이언트를 구분하기 위해 세션 ID를 부여하며, 브라우저가 서버에 접속해서 종료될 때까지 인증상태를 유지합니다.
- 접속 시간에 제한을 두어 일정 시간 응답이 없으면 정보가 유지되지 않게 설정도 가능합니다.
- 사용자 정보를 서버에 두기 때문에 쿠키보다 보안에는 좋지만, 사용자가 많아질수록 서버 메모리를 많이 차지하게 됩니다.
- 동접자 수가 많은 웹 사이트인 경우, 서버에 과부하를 주게 됨으로 성능 저하의 원인이 됩니다.
- 클라이언트 Request를 보내면, 서버의 엔진이 클라이언트에게 유일한 ID를 부여합니다. 이 ID가 세션ID 입니다.
세션의 특징
- 각 클라이언트에게 고유ID 부여
- 세션 ID로 클라이언트를 구분해서 클라이언트의 요구에 맞는 서비스를 제공
- 보안 측면에서 쿠키보다 우수
- 다만, 사용자가 많아질수록 서버에 과부하를 주게 됨 (서버 메모리 차지)
동작 방식
- 클라이언트가 서버에 접속 시 세션 ID를 발급 받음
- 클라이언트는 세션 ID에 대해 쿠키를 사용해서 저장하고 가지고 있음
- 클라리언트는 서버에 요청할 때, 이 쿠키의 세션 ID를 같이 서버에 전달해서 요청
- 서버는 세션 ID를 전달 받아서 별다른 작업없이 세션 ID로 세션에 있는 클라언트 정보를 가져와서 사용
- 클라이언트 정보를 가지고 서버 요청을 처리하여 클라이언트에게 응답
4. 쿠키와 세션의 차이
쿠키와 세션은 비슷합니다. 그 이유는 세션도 결국 쿠키를 사용하기 때문입니다.
가장 큰 차치점은 사용자의 정보가 저장되는 위치입니다. [ 쿠키 : 클라이언트(로컬) | 세션 : 서버 ]
보안 면에서는 세션이 쿠키보다 우수하지만 요청 속도는 세션보다 쿠키가 더 빠릅니다. (세션은 서버의 처리가 필요)
사용자 정보 저장 위치
보안
- 쿠키 : 쿠키는 로컬에 저장되기 때문에 변질되거나 request에서 스니핑 당할 우려가 있어 보안에 취약합니다.
- 세션 : 세션은 쿠키를 이용해서 세션ID만 저장하고 그것으로 구분해서 서버에서 처리하기 때문에 비교적 보안성이 좋습니다.
유효시간
- 쿠키 : 유효시간이 있고 파일로 저장되기 때문에 쿠키를 삭제하지 않는 이상 브라우저가 종료되도 유효시간까지 정보가 남아 있을 수 있습니다. (로컬에서 관리)
- 세션 : 세션도 유효시간을 설정할 수는 있지만, 브라우저가 종료되면 설정된 유효시간이 만료되지 않았더라도 삭제됩니다. (서버에서 관리)
속도
- 쿠키 : 쿠키파일에 정보가 있기 때문에 서버에 요청시 속도가 빠릅니다.
- 세션 : 정보가 서버에 있기 때문에 처리가 요구되어 비교적 느린 속도를 가집니다.
5. 쿠키와 세션의 보안상 문제
보안상 대비를 하지 않으면 세션 하이재킹에 취약합니다.
세션 하이재킹은 로그인된 상태를 가로채는 공격 기법입니다.
세션 하이재킹에 쿠키/세션 뿐만아니라 토큰 인증방식도 안전하지 않습니다.
대응 방법
- 쿠키의 암호화 : 공격자가 탈취정보를 추측하기 어렵게 하기 위해, 쿠키 값을 암호화하여 저장합니다.
- HTTPS 통신 활용 : SSL 인증 기술을 이용해서 암호화 프로토콜인 HTTPS를 사용하여 전송합니다.
- 만료 정책 : 쿠키 / 세션의 유효시간을 짧게 설정하여, 장시간 사용자 정보가 이용되는 것을 방지합니다.
6. 토큰 (Token) & JWT (JSON Web Token)
토큰?
유저가 인증에 성공하면 서버는 토큰을 생성해서 클라이언트로 전달합니다.
토큰과 세션의 방식을 비슷한데 차이가 있습니다.
세션 인증에서는 서버가 세션ID를 저장하고 클라이언트가 쿠키로 보낸 세션ID와 대조해서 확인하지만,
토큰을 사용하면 요청을 받은 서버는 토큰이 유효한지만 확인 합니다.
토큰 인증이 세션에 비해 서버에 부하가 덜 합니다.
JWT
JWT는 웹표준(RFC7519)으로, 두 개체에서 JSON 객체를 사용하여 가볍고 자가수용적인(self-contained)방식으로 정보를 안전성 있게 전달합니다.
JWT는 C, Java, Python, C++ 등 다양한 프로그래밍 언어에서 지원 됩니다.
JWT는 필요한 모든 정보를 자체적으로 가지고 있습니다. JWT 시스템을 통해 발급된 토큰은, 토큰에 대한 기본정보, 전달정보 그리고 토큰이 검증되었다는 singature를 포함합니다.
JWT는 웹서버의 경우 HTTP 헤더에 넣거나 URL의 파라미터로 쉽게 전달 할 수 있습니다.
토큰을 만들기 위해 필요한 3가지
-
Header : 토큰의 타입과 해싱 알고리즘을 지정합니다.
- 토큰의 타입은 JWT 입니다.
- 해싱 알고리즘은 보통 HMAC-SHA256(HS256) 또는 RSA가 사용되며, 토큰을 검증할 때 사용되는 signature에서 사용됩니다.
-
payload : 토큰에 담을 정보가 담겨있습니다. 일반적으로 유저의 고유 ID값, 유효기간이 담겨있습니다.
그리고 여기에 담는 정보의 한 조각을 클레임(claim)이란 단위로 부르고, 클레임은 name/value 한 쌍으로 구성되어 있습니다.
토큰에는 여러개의 클레임들을 넣을 수 있습니다.
1. 등록된(registered) 클레임 : 서비스에 필요한 정보들이 아닌, 토큰에 대한 정보들을 담기위해 이름이 이미 정해진 클레임입니다. 모두 선택적(optional)입니다.
- iss: 토큰 발급자 (issuer)
- sub: 토큰 제목 (subject)
- aud: 토큰 대상자 (audience)
- exp: 토큰의 만료시간 (expiraton), 시간은 NumericDate 형식으로 되어있어야 하며 (예: 1480849147370) 언제나 현재 시간보다 이후로 설정되어있어야합니다.
- nbf: Not Before 를 의미하며, 토큰의 활성 날짜와 비슷한 개념입니다. 여기에도 NumericDate 형식으로 날짜를 지정하며, 이 날짜가 지나기 전까지는 토큰이 처리되지 않습니다.
- iat: 토큰이 발급된 시간 (issued at), 이 값을 사용하여 토큰의 age 가 얼마나 되었는지 판단 할 수 있습니다.
- jti: JWT의 고유 식별자로서, 주로 중복적인 처리를 방지하기 위하여 사용됩니다. 일회용 토큰에 사용하면 유용합니다.
2. 공개 (public) 클레임 : 충돌이 방지된(collision-resistant) 이름을 가지고 있어야 됩니다. 충돌을 방지하기 위해, 클레임 이름을 URI 형식으로 짓습니다.
3. 비공개 (private) 클레임 : 등록되거나 공개된 클레임이 아닙니다. 클라이언트와 서버간 협의하에 사용하는 클레임 이름들입니다. 공개 클레임과 달리 이름이 중복되어 충돌이 날 수 있으니 사용에 유의해야 합니다.
-
Signature : header의 인코딩 값과 payload의 인코딩 값을 각각 base64로 인코딩하고, 인코딩한 값을 비밀 키를 이용해서 헤더에서 정의한 알고리즘으로 해킹 한 후, 이 값을 다시 base64로 인코딩하여 생성합니다.
JWT의 장점
- JWT는 발급한 후 검증만 하면 되기 때문에 추가 저장소가 필요 없습니다. 그래서 세션/쿠키와 달리 저장소 관리가 필요하지 않아 간편합니다.
- 쿠키/세션처럼 정보를 저장하며 상태를 가져가지 않기 때문에, Stateless한 서버를 구성할 수 있습니다.
- 토큰 기반으로 하는 다른 인증 시스템에 접근이 가능하여 확장성이 뛰어납니다. Goggle 로그인, Facebook 로그인 등 모두 토큰 기반으로 인증하기을 하고 이름과 이메일 같은 정보를 받을 수 있는 권한도 추가 할 수 있습니다.
JWT의 단점과 주의사항
- 자가수용적(Self-contained)이라 토큰 자체에 정보들을 담고 있어, 보안상 내 정보도 탈취당할 수 있습니다.
- 토큰의 페이로드에 3종류의 클레임을 저장하기 때문에, 정보가 많아질수록 토큰이 길이가 늘어나 네트워크에 부하를 줄 수 있습니다.
- 페이로드 자체는 암호화 된 것이 아니라 base64로 인코딩 된 것입니다. 중간에 페이로드가 탈취당해서 디코딩하면 정보들을 볼 수 있으므로, JWE로 암호화하거나 페이로드에 중요한 정보를 넣지 않아야 합니다.
- JWT는 상태를 저장하지 않기 때문에(Stateless) 한번 생성되면 제어가 불가능합니다. 토큰을 임의로 삭제하는 것이 불가능하므로, 토큰 만료 시간을 꼭 지정해주어야 합니다.
- 토큰의 만료시간이 길다면, 사용자가 아닌 타인이 만료시간 전 토큰을 가지고 예민한 유저정보들을 탈취할 수 있습니다. 이를 방지하기 위해 토큰의 만료시간을 짧게 합니다. 그리고, 실제 인증에 사용되는 Access Token과 Access Token을 재발급해주는 Refresh Token을 나눠 생성하여 Access Token이 만료될 시에 Refresh Token으로 Access Token을 재발급 해서 사용하는 방식을 채택합니다.