해당 포스팅은 웹 개발에서 빼놓을 수 없는 기능인 로그인에 대해 다룬다.
로그인 기능을 몇 차례 구현해봤지만 기능을 구현하며 쿠키와 세션, JWT 등 어떤 방식으로 로그인을 구현할 것이며 이들의 차이에 대해 고민해본 적이 없다.
로그인 방식을 선택하는 것은 백엔드 개발자의 몫이고 프론트엔드는 그저 로그인 기능을 잘 구현해서 유저가 불편하지만 않게 만들면 된다는 생각이 있었기 때문이다.
하지만, 로그인을 이해하는 것은 웹 개발 전반에 대한 이해도를 높일 수 있으며 보안에 대한 이해도, 사용자 경험 향상 등 프론트엔드 개발자에게도 많은 도움이 될 것이다.
따라서 이번 포스팅에서는 로그인에 주로 활용되는 쿠키와 세션, JWT의 차이점과 이 둘의 장단점에 대해 알아본다.
이번 포스팅은 이론에 대해서만 다룰 것이며, 3월 원티드 F.E. 프리온보딩(로그인) 수강 이후 실습 파트에 대한 내용을 포스팅 할 것이다.
클라이언트와 서버는 HTTP 프로토콜
을 사용하여 통신한다.
HTTP 프로토콜
에는 비연결성(Connectionless)
와 무상태성(Stateless)
라는 특징이 있다.
HTTP 프로토콜
은 요청과 응답을 처리하기 위해 각각의 요청이 독립적인 연결을 생성하고 종료한다.
이러한 특징을 비연결성
이라고 하며, 비연결성
으로 인해 클라이언트에서 로그인에 성공하더라도 이전 요청에 대한 데이터를 잊어버린다.
이를 무상태성
이라고 한다.
HTTP 프로토콜
의 비연결성
과 무상태성
특징을 해결하고 클라이언트와 서버 간 상태 정보를 유지하기 위해 쿠키와 세션
방식을 사용한다.
HTTP 프로토콜
에 대한 자세한 내용은 이후 포스팅에서 다루도록 한다.
쿠키는 클라이언트
측에서 상태 정보를 저장하는 방식이다.
서버는 사용자의 웹 브라우저에 쿠키를 보내며 브라우저는 서버에서 보낸 쿠키를 저장하고 이후 요청과 함께 동일한 서버로 다시 전송할 수 있다.
쿠키는 주로 세션 관리
, 개인화
, 트래킹
을 위해 사용된다.
쿠키는 다음과 Set-Cookie
를 사용하여 생성할 수 있다.
Set-Cookie: <cookie-name>=<cookie-value>
위의 방식으로 생성한 쿠키는 아래와 같이 HTTP header
를 통해 클라이언트에 전송된다.
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry
클라이언트는 서버에서 전송받은 쿠키를 저장하며, 이후 서버에 보내는 요청마다 HTTP Header
에 Cookie
를 포함하여 전송한다
GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry
즉, 생성된 Cookie
는 모든 요청에 포함되어 전송되기 때문에 성능이 떨어지는 원인이 될 수 있다.
이를 해결하기 위해, HTML5
에서는 웹 스토리지 API(localStorage
, sessionStorage
)또는 indexdDB
를 사용한다.
이에 대해서는 뒤에서 알아보자.
세션 쿠키
는 브라우저를 닫으면 삭제되는 쿠키를 말한다.
만료 기간을 설정하지 않으면 세션 쿠키
로 동작한다.
주로 인증 정보
등의 임시 데이터
를 저장하는데 사용한다.
Max-Age
또는 Expires
속성을 통해 만료 기간을 설정한 쿠키를 말한다.
영속 쿠키
는 브라우저가 종료되도 삭제되지 않으며 만료 기간이 지나면 자동으로 삭제된다.
주로 사용자 설정
이나 환경
등을 저장하는데 사용한다
Secure
과 HttpOnly
속성은 쿠키의 보안을 강화하기 위해 사용된다.
쿠키를 HTTPS(SSL/TLS)
프로토콜을 통해서만 전송하도록 설정하는 속성이다.
HTTPS 프로토콜을 사용하면 클라이언트와 서버 간의 통신 내용이 암호화되므로 쿠키를 탈취하는 공격을 어렵게 만든다.
따라서 로그인 정보와 같은 민감한 정보
를 다룰 때는 반드시 secure
속성을 설정해야 합니다.
하지만, Secure
속성을 사용하더라도 민감한 정보는 쿠키에 저장하면 안된다.
쿠키가 JavaScript
의 Document.cookie
API로 부터 접근될 수 없도록 설정하는 속성이다.
이 속성을 사용하면, 웹 사이트가 XSS(Cross-Site Scripting)
공격을 방지한다.
이외에도 Domain
, Path
, SameSite
속성이 있다.
참고자료
MDN HTTP Cookie
https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies
앞서 살펴본 쿠키
는 클라이언트에 저장되고 관리된다.
이러한 특징은 서버의 자원을 사용하지 않기 때문에 서버의 부하
를 줄일 수 있다는 장점이 있다.
하지만 사용자의 개인 기기에 저장되기 때문에 쿠키 정보가 탈취되거나 임의 조작되는 등 보안의 위험
이 존재한다.
따라서 민감한 정보를 쿠키
에 저장하는 것은 위함하다.
이를 해결하기 위해 세션
이라는 개념이 등장한다.
세션은 쿠키를 기반으로 동작하지만, 정보를 서버
측에서 관리한다는 점에서 차이가 있다.
서버는 클라이언트에 Session ID
값을 부여하고 정보는 서버에 저장한다.
이후 클라이언트의 요청에 따라 서버에서 Session ID
를 조회하여 클라이언트 정보를 가져와 사용한다.
로그인 정보
, 장바구니 정보
서버에 저장해야 하는 정보를 클라이언트가 매번 보내지 않고, 세션에 저장하여 관리한다.
사용자 인증
로그인에 성공한 사용자는 서버에서 생성한 세션 ID를 사용하여 인증을 진행한다.
쿠키와 세션의 가장 큰 차이점은 사용자의 정보가 저장되는 위치
이다.
쿠키는 클라이언트
, 세션은 서버
에 저장된다.
보안은 세션
이 우수하고, 속도는 쿠키
가 더 빠르다.
쿠키
는 브라우저를 종료해도 유지될 수 있으며, 만료기간이 종료되어도 브라우저가 해당 쿠키를 인식하지 못하게 되는 것일 뿐 파일 자체가 삭제되지 않는다.
세션
은 브라우저의 메모리에 저장되므로 브라우저가 종료되면 삭제된다.
단, 서버 측 세션
이 사라지는 것이 아니라 클라이언트 측 세션ID
가 사라지는 것이다.
서버
측에 데이터를 저장하기 때문에 사용자가 많아지면 서버에 부하가 심해진다.서버
가 에러가 발생하는 경우 세션
에 저장된 데이터가 모두 손실된다.로드밸런싱
을 통한 서버를 확장시, 세션 관리가 어렵다.중복 로그인 처리
등 구현이 복잡하다.이러한 쿠키/세션 방식의 단점을 극복하기 위해 JWT(Json Web Token)
방식이 도입됐다.
JWT
는 웹 토큰 기반의 인증 방식 중 하나이다.
기존의 쿠키/세션
방식의 단점인 서버 측 자원 소모와 확장성이 제한된다는 등의 단점을 보안하기 위해 등장하였다.
서버 측에서 토큰을 발급하여 클라이언트에게 전달하면, 이후에는 클라이언트 측에서 해당 토큰을 가지고 인가
를 수행한다.
즉, 서버는 세션
을 유지할 필요가 없으며 클라이언트는 토큰
을 사용하여 언제든 인증이 가능하다.
Authentication(인증) vs Authorization(인가)
인증
은 사용자가 누구인지 확인하는 과정으로, 사용자가 제공한 정보를 검증하여 사용자의 신원을 확인한다.
인가
는 인증된 사용자가 특정 자원에 접근할 수 있는 권한이 있는지를 확인하는 과정이다.
이때, 사용자의 인증 정보를 이용하여 검증한다.
JWT
의 인증 방법은 Cookie
와 유사하지만 몇 가지 차이점이 있다.
JWT | 쿠키 | |
---|---|---|
전송 방식 | Authorization 헤더 | Cookie 헤더 |
보안 | 서명(signature )된 토큰 사용(위조 방지) | 서명되지 않은 값(조작 가능) |
서버 확장성 | 서버에서 세션을 관리하지 않아도 됨 | 서버에서 세션을 관리하므로 확장성이 떨어짐 |
만료 시간 | 만료 시간을 포함시킬 수 있으며 토큰이 만료되면 자동으로 만료됨 | 만료 시간을 설정해도 브라우저를 닫거나 쿠키를 삭제하기 전까지 유지됨 |
JWT
는 각각의 구성 요소( HEADER
, PAYLOAD
, SIGNATURE
)가 .
으로 구분되어 있다.
참고자료
JWT
https://jwt.io/
헤더에는 토큰의 타입과 SIGNATURE
생성에 사용된 알고리즘이 담긴다.
"alg"
: 사용된 알고리즘 (예: HMAC SHA256 또는 RSA)"typ"
: 토큰의 타입 (JWT)페이로드는 Claim(클레임)
이라고도 불리며 사용자의 정보와 같은 데이터를 담는다.
표준으로 정해진 Claim
은 다음과 같다.
registered claim
"iss"(issuer)
: 토큰 발급자"sub"(subject)
: 토큰 제목(주로 유저 식별자 등)"aud"(audience)
: 토큰 대상자"exp"(expiration time)
: 토큰 만료 시간"nbf"(not before)
: 토큰 활성화 시간"iat"(issued at)
: 토큰 발급 시간"jti"(JWT ID)
: JWT 고유 식별자public claim
공개용으로 사용하기 위한 claim
으로 특정 프로젝트나 조직에서 사용하는 claim
을 자유롭게 정의해서 사용할 수 있다.
private claim
JWT를 사용하는 개발자나 프로젝트에서만 사용되는 claim을 정의해서 사용할 수 있다.
이를 위해 claim 이름 앞에 일반적으로 발행자(issuer)의 도메인 이름을 추가해 claim 충돌을 방지하는 것이 좋습니다.
단, Payload
는 암호화되지 않기때문에 민감한 정보를 포함하면 안된다.
시그니처는 Header
와 Payload
를 합친 문자열과 서버에서 발급한 Secret Key
를 사용하여 생성된다.
이를 통해 JWT의 위변조
여부를 확인한다.
인코딩
되어 있지만 암호화
되어 있지 않기 때문에, 정보가 노출될 위험이 있다.웹스토리지에 대한 내용이 추가될 예정입니다.