먼저 로그인을 구현하는 인증방식으로 Session(세션인증)과 JWT(토큰인증)가 있습니다.
비교적 최신에 나온 JWT는 많은 기술 블로그에서도 소개가 되면서 무조건 기본으로 채택하는 경우가 있습니다.
그런데 정말 JWT가 좋으니깐 기본으로 채택해야 할까요?
먼저 세션 - 토큰 인증방식의 트레이드오프를 살펴보기 위해 각 특징에 대해서 간단히 살펴보고자 합니다.
: 서버나 DB에 사용자 인증정보 저장하는 방식
: 클라이언트 측에 사용자 인증정보 저장하는 방식
세션은 쿠키에 세션 ID만을 담아 전송하기 때문에 트래픽이 적게 사용됩니다. 참고 글에서의 비교를 보면 세션의 크키는 6 byte인 반면, JWT(최소한의 정보-iss
, sub
, nbf
, exp
, ìat
, jti
, typ
)는 304 byte로 세션보다 약 50배 이상의 크기를 보유하고 있다. 따라서 JWT가 상대적으로 크기 때문에 쿠키에서 사용될 때, 요청 당 오버헤드가 발생될 수 있다는 점이 있습니다.
만약 해커가 토큰을 탈취했다고 한다면, 토큰이 만료되지 않는 한 해커는 서버에 요청을 할 수 있게 됩니다. 그리고 서버의 무상태성 특징때문에 토큰이 탈취돼도 로그아웃을 강제로 못하는 문제가 있습니다.
이러한 문제를 보안하기 위해서 다음의 작업들을 수행할 수도 있습니다.
Q) refresh 토큰이 탈취돼서 이를 로그아웃을 시키기 위해 서버 저장소를 두게 된다면?
A) refresh 토큰 검증을 위해 중앙 집중식 저장소가 필요하게 된다. 그렇다면 서버 저장소, 클라이언트 저장이 필요해지면서 JWT의 최대 장점이었던 무상태성이 무효화됩니다. 따라서 세션과 유사한 방식으로 관리하게 됩니다.
브라우저의 웹 저장소(LocalStorage, SessionStorage)나 쿠키에 토큰을 저장하게 되면 XSS, CSRF 공격에 취약하다는 문제가 발생합니다.
저장하는 방법이 편리하긴 하지만 자바스크립트에서 document.cookie
으로 접근이 가능하게 되면서 XSS(Cross Site Scripting) 공격에 취약하게 되므로 비권장됩니다.
쿠키에 옵션을 주면 XSS, CSRF 공격에 조금 안전해질 수 있습니다.
httpOnly
: XSS 공격에 조금 안전 (JS에서 document.cookie
로 접근 불가)secure=true
: HTTPS
일때만 쿠키 전송가능sameSite=strict
: CSRF(Cross-Site Request Forgery 사이트 간 요청 위조) 방지.lax
none
secure
옵션 붙여야 작동함strict
😱 쿠키 secure 설정 시 주의사항
- 같은 도메인이더라도
(FE 로컬)http - (서버)https
환경에서는 refresh 쿠키 전달 못받음.
서로 scheme이 달라서 다른 사이트로 인식한다. FE 로컬에서 작업할 때는 추가 작업이 필요하다.😱 쿠키 sameSite 설정 시 주의사항
- BE 서버가 FE와 같은 도메인일때만 사용가능.
배포환경은 BE-FE 같은 도메인이라 괜찮지만
개발환경에서 BE, FE가 다르면 크로스도메인 이슈 발생- 추가 세팅 필요
도메인이 서로 다르면 서버에서 cors header를 설정하거나 refresh 토큰 요청을 인증된 사이트에서만 하도록 세팅해줘야한다.
FE에서axios.defaults.withCredentials = true;
설정도 필요하다.
access 토큰 저장소 - 로컬 변수로 저장
refresh 토큰 - secure httpOnly sameSite
쿠키에 저장
httpOnly
- XSS에 조금 안전secure
- HTTPS으로만 접속 가능sameSite=strict
- CSRF에 조금 안전httpOnly secure sameSite
옵션 설정한 쿠키로 저장하여 전송.Bearer ${accessToken}
의 규격으로 전송./refresh
)에 요청하고 응답으로 Access 받아서 갱신하기/refresh
)에 요청하고 응답으로 Access 받아서 갱신하기결국 상황에 맞게 고려해서 방식을 채택해야 합니다. 서비스의 규모, 동시 접속자 수, 보안이 중요한 서비스 등등 상황들을 따져보고 선택하거나 두가지 인증 방식을 결합하여 구현할 수도 있습니다.
각각의 트레이드 오프가 무엇인지 확인해보면서 전통적인 방식의 세션과 비교적 최신방식의 토큰 중 상황에 맞춰서 선택할 필요가 있습니다.
따라서 JWT를 무조건 깃헙의 템플릿(보일러플레이트)에 기본으로 구성한다던지 앞으로의 프로젝트에 사용할 지는 충분히 고민을 해 본 후에 적용하는 것이 적절하다고 생각했습니다.
만약, 보안이 중요한 서비스에서 강제 로그아웃을 구현해야한다면 서버의 refresh 저장소 구축에 대해서는 결국 세션과 유사한 방식이 되기 때문에 이렇게 된다면 그냥 세션을 사용하면 되지 않을까라는 생각이 들었습니다.
또한 여기서 소개한 방법들이 모두 완벽한 정답은 아니므로 참고한다는 의미로 봐주시고 혹시나 잘못된 정보가 있었다면 자유롭게 댓글로 남겨주시면 감사하겠습니다.