Session 과 Token 기반 인증방식의 차이

rvlwldev·2023년 3월 16일
0

CS

목록 보기
6/12

과거 세션을 이용한 인증/인가 방식이 주된 방법이였으나, 어느샌가 부터 규모 또는 자원의 한계로 인해 JWT (Json Web Token) 방식 또한 많이 사용하고 있다.
이 두 방식의 가장 큰 차이점인 Stateful, Stateless (참고) 의 차이만을 넘어서 조금 더 알아볼 필요가 생겼다.

Session이란?

일단 세션이란 서버와 클라이언트간의 상호작용을 위한
일시적인 시간동안 유지되는 저장공간, 상태, 값(하나의 단위로 본다)이라고 정의하고 있다.
이 세션은 특정된 클라이언트가 방문한 시점부터 연결이 끊길 때까지 유지된다.

세션은 보통 Key-Value 형식으로 이루어져 있으며 Key에는 sessionId
value는 사용자 정보가 저장된다.

인증(Authentication) 후 생성된 세션으로 인가(Authorization)의 과정을 거치게 된다.

(Authentication : 신원을 확인하는 과정)
(Authorization : 특정 접근/요청을 제어하는 과정)

서버에서 생성된 세션은 클라이언트의 쿠키(Cookie)에도 저장된다.
이후 사용자의 모든 요청은 해당 쿠키와 함께 서버로 전송되며
서버는 쿠키에 저장된 세션정보를 통해 해당 사용자의 세션(SessionId)을 식별하고
요청에 대한 응답을 생성한다.

쿠키(Cookie)란?
사용자의 브라우저에 저장되는 작은 데이터 조각이며 일반적으로 서버에서 특정 정보를 추적하기 위해 사용된다.
쿠키는 이름과 값, 만료날짜, 도메인, 세부경로 등의 정보를 담고 있다.

Session기반 인증방식

  1. 클라이언트가 유효한 아이디와 비밀번호로 로그인을 요청하면
    세션이 서버의 메모리 상에 저장된다.
  2. 서버에서 클라이언트에게 세션 식별값(SessionId)를 쿠키에 담아 전달한다.
  3. 이후 클라이언트는 모든 요청에 쿠키와 함께 전송한다.
  4. 서버는 요청에따라 SessionId를 통해 세션 검증을 거친 뒤 알맞는 응답을 반환한다.

해당 과정을 그림으로 표현하면 아래와 같다.

세션기반인증

그림출처 LG CNS

장점

1. 성능 및 보안성

서버에 로그인 정보를 저장하기에 생기는 장점들이다.
클라이언트의 매 요청마다 로그인 정보를 전송할 필요가 없기 때문에 성능을 향상시키는데 이점이 있다.
또한 브라우저에서 인증 정보를 노출시키는 위험을 줄일 수 있으며
서버에서 SessionId를 삭제하여 로그아웃 기능도 안전하게 구현할 수 있기때문에 보안성을 높일 수 있다.

2. 유연성

세션 식별값을 가진다는건 클라이언트와 서버 간의 논리적 연결을 가지고 있다는 의미이기도 하다.
이러한 유연성은 다양한 기능을 구현하는 데 유용할 수 있다.
예시로는 여러 페이지를 제공하는 사이트의 경우,
세션 식별값으로 각각의 페이지에서 사용자 맞춤 동적페이지를 제공하기 쉽다.

단점

1. 서버의 부담과 확장성

서버에서 직접 저장하기 때문에 사용자가 많아지게 된다면 서버의 메모리가 부족해 질 수도 있다.
이러한 과부하를 방지하기 위해 JVM의 Heap사이즈를 늘리거나 DB에 저장을 하기도 하는데, 모든 사용자의 세션정보를 직접 서버에 저장/관리하는건 변함이 없기 때문에 서버 확장의 부담을 느끼기 쉽다.

결국 사용자가 늘어나게 되면 원활한 접속을 위해 서버를 확장해야 할 것이다.
세션인증방식을 선택했다면 세션을 분산시키는 설계해야 하지만 (세션 클러스터링, 로드밸런싱 등)
이러한 과정은 고려해야할 사항도 많고 복잡한 편이기 때문에 많은 비용과 시간이 소모될 수 있다.

Session Clustering이란?
WAS를 여러대 사용할 경우 세션을 공유하여 서로 동일한 세션을 관리하는 것

Load Balancing이란?
서버가 처리해야 할 업무 혹은 요청(Load)을 여러 대의 서버로 나누어(Balancing) 처리하는 것

2. CORS(Cross-Origin Resource Sharing)

CORS는 간단하게 다른 도메인의 정보를 요청할때 여러 보안상의 이유로 제한이 걸리는 문제이다.

세션인증방식에서는 서버에서 발급한 쿠키를 사용하여 세션 정보를 유지한다.
쿠키는 해당 도메인과 서브도메인에서만 사용할 수 있게 설계되어 있기 때문에
다른 도메인에서 실행되는 스크립트가 해당 도메인의 쿠키를 읽거나 수정할 수 없는 CORS 문제가 발생할 수 있다.

그래서 클라이언트와 서버가 다른 도메인을 쓸 때 데이터를 주고 받으려면
Access-Control-Allow-Origin 정보를 Header에 추가해줘야 하는 번거로움이 있다.

Token이란?

여기서 토큰이란 티켓과 비슷한 뜻을 가진 영단어 토큰과 거의 같은 의미를 가진다.
영화표처럼 인증이 완료된 클라이언트에게 이 토큰을 발급하고 서버에 요청을 할 때 이 토큰과 함께 전송하며 서버에서 토큰 유효성 검사를 한 후 응답을 반환한다.

이 토큰은 위 세션기반 인증방식의 확장성과 CORS문제의 단점을 상쇄시킬 수 있기 때문에 자주 사용되며 Stateless하다.
(주로 JWT(Json Web Token), OAuth 등이 자주 사용된다.)

OAuth란? (Open Standard for Authorization)
2006년 Twitter와 Google이 정의한 개방형 Authorization 표준이며, API 허가(Authorize)를 목적으로 JSON(JavaScript Object Notation) 형식으로 개발된 프레임워크이다.

현재는 다양한 인증 서버와 프로토콜을 지원하므로, 인증 및 권한 부여를 위한 프로세스를 구현하는 데 유연성을 제공하며, 이는 웹, 모바일, 클라우드 등 다양한 플랫폼과 서비스에서 사용되고 있다.

출처1, 출처2

Token기반 동작방식

  1. 사용자가 아이디와 비밀번호로 로그인을 하면,
    서버에서 고유한 접근토큰(Access Token)을 발급한다.
  2. 클라이언트는 서버 측에서 전달받은 토큰을 쿠키나 스토리지에 저장해 두고,
    서버에 요청을 할 때마다 토큰을 HTTP 요청 헤더에 포함시켜 전달한다.
  3. 서버는 전달받은 토큰을 검증하는 과정을 거친 뒤 요청에 응답한다.
  4. 토큰에는 요청한 사람의 정보가 담겨있기에 서버는 DB를 조회하지 않고
    누가 요청하는지 알 수 있다.

토큰그림
그림 출처

장점

1. 서버에서 인증정보를 별도로 저장할 필요가 없다.

Stateless하다 라고도 말하며 서버와의 논리적 연결이 없기에
세션기반 인증방식보다 메모리를 호율적으로 사용할 수 있다.
이 장점은 곧 세션기반 인증방식에서 서버확장시 세션 클러스터링이나 로드 밸런싱 등의 처리가 필요하지만
토큰방식을 사용할 경우 해당 토큰의 유효성 검증만 하면 되기 때문에 서버 확장이 비교적 수월하다
즉, 확장성(Scalability)이 높다고 말할 수 있다.

2. 높은 확장성(Extensibility)

다른 시스템과 인증 및 권한 공유가 가능하다. 만약 OAuth를 활용한다면,
페이스북이나 구글 등과 같은 소셜 계정을 이용하여 다른 서비스에서도 로그인이 가능하다.

여러 디바이스, 도메인에서도 호환되기 쉽다. (위 세션인증방식에서 CORS 문제 또한 해결된다)
각 토큰들은 인증정보를 담고 있기 때문에 유효한 토큰이라면 디바이스와 상관없이 응답을 반환받을 수 있다.
즉, 결합도가 낮다고 말할 수 있으며 서버에서 해당 토큰의 대한 유효성을 검증할때도 DB조회를 안해도된다.

ScalabilityExtensibility의 차이
둘다 확장성으로 번역되지만 확연히 다른의미를 가진다

Scalability는 시스템이 얼마나 많은 작업, 데이터 등을 처리할 수 있는지의 관한 능력을 의미한다.
리소스를 추가하거나 분산 시스템으로 확장하는 방식으로 높은 Scalability를 가질 수 있다.
위 설명에서는 서버의 물리적 확장성을 뜻한다.

Extensibility는 시스템이 구성 요소들 사이에서 결합도를 낮춰
새로운 요구사항을 얼마나 쉽게 수용되고 변경, 확장될 수 있는지를 의미한다.
위 설명에서는 하나의 소셜로그인 기능으로 다양한 도메인/서비스에서
로그인 기능을 구현할 수 있다는 것을 의미한다.

단점

1. 통제가 까다롭다.

위 장점인 여러 디바이스, 도메인에서도 호환이 쉽지만
사용자가 여러 기기나 브라우저에서 로그인할 때 각각의 기기에서 다른 토큰을 발급할 수 있다.
이 경우 서비스에 따라 각각의 토큰을 관리하고 유효성을 유지하는 것이 까다로울 수 있다.

또한 토큰은 기본적으로 유효기간이 있다.
보안 등의 이유로 유효기간을 설정하고 관리하는 것이 중요하지만
너무 짧은 경우 사용자 입장에서 너무 빈번하게 로그인을 해야되기에
사용자 경험이 저하될 수 있다.

토큰방식의 특성상 세션방식과 달리 서버에서 로그아웃등의 강제만료 등의 제재 기능은 구현이 어렵다.

2. 보안

토큰을 탈취당하면 대처하기 어렵다.
이런 문제를 보완하기 위해 유효기간을 짧게 설정한다면
사용자 입장에서 너무 빈번하게 로그인을 해야되기에
사용자 경험이 저하될 수 있다.
따라서 XSS 공격 등으로 토큰이 한번 탈취된다면 유효기간동안 계속 요청을 보낼 수 있다.
(이 경우 보통 토큰을 Access Token과 Refresh Token으로 분할함으로써 보완할 수 있다. 참고)

JWT(Json Web Token) 이란?

토큰기반 인증방식으로 JWT가 많이 사용되며 세션기반 인증방식은 요청이 들어올때마다
세션 저장소를 조회해야하는 단점을 보완하기 위해 등장했다.

서버와 클라이언트 사이에서 이 토큰이 Base64로 인코딩된 형태로 전달되는게 일반적이다.
정확하게는 Base64 URL Safe Encoding 방식으로 +, / 문자를 -, _ 로 replace 되고
=를 제거하여 URL에서 안전하게 전송된다.

JWT는 사용자 인증을 포함한 모든 필요한 정보를 JWT 자체가 가지고 있다. (자가수용적, Self-contained)
때문에 별도 저장소에 정보를 저장해둘 필요가 없다는 장점이 있고
보안적 문제는 Signature로 어느정도 보완하였다.

JWT의 Signature는 서버에 있는 개인 고유키로만 암호화를 풀 수 있으므로
기본적으로 다른 클라이언트는 임의로 Signature를 복호화할 수 없다.

JWT의 구조

기본적으로 .을 구분자로 하여 3가지의 문자열(String)로 설계되어 있으며
각각의 부분을 part라고 한다.
ex) hhhhhh.pppppp.cccccc

1. Header

기본적으로 typ, alg 두가지 정보를 담고 있다.

typ은 토큰의 타입을 지정한다. 보통 JWT 표준에서 정의된 타입인 'JWT'를 그대로 사용한다.
(상황에 따라 AT+JWT 등으로 표기하거나 JWE(JSON Web Encryption)로 지정할때도 있다. 참고)

alg는 해싱 알고리즘을 지정한다.보통 HMAC SHA256, RSA 등이 자주 사용된다.
이 값은 Signature(서명)에서 토큰을 검증 할 때 사용된다.

2. Payload(정보)

Payload는 토큰의 정보가 담긴다. 해당 정보를 Claim 이라고 부르고, name-value의 형태로 이루어져 있으며 토큰에는 여러개의 클레임들이 담길 수 있다.

크게 클레임은 다시 3개로 분류될 수 있는데
각각 Registered, Public, Private Claim으로 나뉜다.

  • Registered Claim (등록 클레임)
    이 클레임은 토큰 자체의 정보들을 담기 위해 이름이 이미 등록된(Registered) 클레임이다.
    크게 Issuer, Subject, Audience 다시 나뉘어 지며
    이외에도 추가적으로 Expiration Time, Issued At 등을 지원한다.
    모두 서비스에 따라 선택적(Optional)으로 사용할 수 있다.
    (각각의 데이터들은 약어로 토큰에 담기게 된다.)
    • iss(Issuer): 토큰의 발급자
    • sub(Subject) : 토큰의 주제 또는 제목
    • aud(Audience) : 토큰의 대상
    • exp(Expiration Time) : 만료시간
    • nbf(Not Before) : 사용가능 시작시간
    • iat(Issued At) : 발급시간
    • jti(JWT ID) : JWT의 고유식별자
  • Public Claim (공개 클레임)
    등록되지 않은 자유롭게 정의할 수 있는 데이터들을 말한다.
    공개 클레임은 충돌 저항성을 가진 이름으로 이루어져 있어야하며
    보통 URI의 형식으로 이름을 지어 충돌을 방지하기도 한다.
    이 경우 다른 서버에서 동일한 클레임 이름을 사용하여 충돌나지 않도록 보장할 수 있기에 권장되는 방법이다.
  • Private Claim (비공개 클레임)
    말그대로 일반적으로 공개적으로 공유되지않는,
    보통 사용자 맞춤 데이터나 서버 내부적으로 사용되는 데이터들이 담긴다.
    때문에 각각의 이름은 서버와 클라이언트 사이에서 합의 하에 사용되어야 하며
    이 경우 이름이 다른 클레임과의 이름이 충돌날 위험이 있기에
    JSON 객체로 다시 담는 방법이 많이 사용된다.
// 비공개 클레임 예시
{
"iss" : "Issuer",
"sub" : "sub_01",
"private" : {
		"address" : "South Korea",
        "age" : 20,
  		...
	} // 비공개 클레임!
}

3. Signature(서명)

직역 서명이라는 말에서 유추할 수 있듯이 JWT가 안전하게 사용되기 위해서 변조되지 않았는지 확인하기 위한 용도이다.

해당 Signature는 다음과 같은 순서로 생성된다.

  1. Header와 Payload를 base64(url safe)로 인코딩
  2. 두 값을 . 로 구분하여 붙인다.
  3. 서버의 비밀키와 Header의 "alg" 값으로 선언된 알고리즘으로 해쉬값을 생성
  4. 생성된 해쉬값을 다시 base64(url safe)로 인코딩

https://jwt.io 이 사이트에서 jwt의 인코딩값과 디코딩값을 확인해 볼 수 있다.

profile
ㅇ0ㅇ

0개의 댓글