🌏 인증을 구현하는 방법들
✅ 쿠키 기반 인증
- 쿠키 = 서버가 클라이언트를 구분하기 위해 발급하는 작은 데이터 조각
- 클라이언트 브라우저에 저장되어 이후 요청마다 자동으로 함께 전송
- 보통 서버에서 Set-Cookie 응답 헤더로 내려주고, 브라우저는 이후 요청 시 Cookie 헤더로 전송
- 단순히 상태를 기억하는 용도(장바구니에 넣은 물건을 기억한다던가,,)로도 쓰이지만, 보안 토큰이나 세션 ID 전달에도 활용
쿠키 기반 인증은 초창기 웹에서 가장 많이 쓰였던 방식입니다. 다만 브라우저에 그대로 저장되다 보니 탈취 위험이 있고, 용량이 제한적이며, 보통 도메인 단위로만 사용이 가능합니다.
‘쿠키 기반’이라는 것은 그냥 데이터를 서버로 전달하는 매개체가 쿠키라는 뜻이지, 실제 저장하는 로그인 정보가 서버이면 세션 기반, 모든 정보를 클라이언트가 가지고 있게 되면 JWT 기반으로 나뉘게 됩니다.
또 실제로는 *“쿠키 기반 인증”이라는 표현이 곧 세션 기반 인증을 지칭하는 경우가 많습니다.***
✅ 세션 기반 인증
- 서버가 클라이언트의 로그인 상태(로그인 시각, user id 등등,,)를 서버 메모리나 외부 저장소(Redis, DB 등)에 저장하고, 그 상태의 식별자인 id만 클라이언트에 반환
- 클라이언트는 쿠키에 세션 ID만 담아서 전송
- 서버는 세션 ID를 키로 삼아 저장된 로그인 상태를 조회
세션 기반 인증은 쿠키 단독 방식보다 안전합니다. 클라이언트에는 단순한 세션 키만 남기고, 실제 민감한 정보는 서버 쪽에만 저장되기 때문입니다. 하지만 모든 요청마다 서버가 상태를 조회해야 하므로 Stateless 원칙과 맞지 않습니다.
서버가 세션 정보를 메모리나 저장소에 들고 있기 때문에, 요청이 항상 같은 서버로 가야 하는 Sticky Session 문제가 생깁니다. 이를 해결하려면 Redis 같은 중앙 저장소를 두거나 JWT처럼 Stateless 인증을 쓰는 게 필요합니다.
✅ JWT (JSON Web Token)
- 서버가 클라이언트에게 서명된 토큰을 발급
- 토큰은 세 부분(헤더, 페이로드, 서명)으로 구성
- 클라이언트는 이후 요청마다 Authorization 헤더에 토큰을 담아 전송
- 서버는 서명 검증을 통해 토큰의 위·변조 여부를 확인하고, 별도의 세션 저장소 없이 클라이언트 인증 가능
JWT는 세션과 달리 서버가 상태를 저장하지 않아도 됩니다. 즉, 완전히 Stateless 인증을 구현할 수 있다는 장점이 있습니다. 하지만 토큰이 클라이언트에 그대로 노출되기 때문에 탈취되면 큰 위험이 있고, 토큰이 만료되기 전까지는 강제로 무효화하기가 어렵습니다. 그래서 대규모 서비스에서는 보통 블랙리스트 테이블이나 리프레시 토큰 전략을 함께 사용합니다.
토큰에 들어가는 정보는 사용자 ID, 권한, 만료 시각 등 인증·인가 관련 클레임(claim) 등이고, 비밀번호 같은 민감한 건 안 넣습니다.
✅ OAuth
- 사용자가 직접 아이디와 비밀번호를 입력하지 않고, 다른 서비스(구글, 카카오 등)의 인증을 빌려오는 방식
- 리소스 소유자(사용자), 클라이언트(앱), 인증 서버, 자원 서버 네 가지 주체가 존재
- 대표 흐름은 Authorization Code Grant 방식
- 사용자가 로그인 버튼 클릭 → 인증 서버로 이동
- 인증 서버에서 로그인 후, 클라이언트에 Authorization Code 반환
- 클라이언트가 이 코드를 인증 서버에 제출해 Access Token 발급
- Access Token으로 자원 서버(API)에 접근
OAuth는 흔히 소셜 로그인에서 볼 수 있습니다. 사용자가 “구글로 로그인”을 누르면 구글이 대신 인증을 수행하고, 서비스는 구글이 발급한 토큰을 활용해 사용자 정보를 가져옵니다. 이렇게 하면 서비스가 사용자의 비밀번호를 직접 다루지 않아도 되기 때문에 보안이 강화됩니다.
🌏 클라이언트는 쿠키 이외에 로그인 정보를 어디에 저장할 수 있을까?
많은 분들이 "쿠키 기반 인증", "세션 기반 인증", "JWT 기반 인증"을 헷갈리곤 합니다.
사실 쿠키는 단순히 서버와 클라이언트 간 데이터를 전달하는 매개체일 뿐입니다.
쿠키 이외에도 다양한 곳에 로그인 정보를 저장할 수 있습니다.
세션 기반 인증에서는 로그인 정보를 서버가 메모리나 DB, Redis 같은 저장소에 보관하고, 클라이언트에는 세션 ID만 발급합니다. 이 세션 ID는 로그인 정보를 포함하고 있지 않기 떄문에 토큰에 비해서 빡세게 관리할 필요는 없습니다. (문제 생기면 그냥 서버가 해당 세션을 무효화하면 되기 땜시) 따라서 세션 아이디는 보통 쿠키에 담겨서 자동으로 서버로 전송됩니다.
반면 JWT 기반 인증에서는 로그인 정보가 토큰 안에 직접 들어 있고, 클라이언트가 토큰을 들고 다니며 서버에 증명하기 때문에 해당 정보를 어디에 보관하냐는 보안상 조금이 아니라 많이 중요해질 수 있습니다.
✅ 쿠키에 저장
쿠키는 브라우저에서 관리되며, 기본적으로 같은 도메인으로 요청을 보낼 때마다 자동으로 서버로 전송됩니다. 이 특성 덕분에 인증 흐름이 단순해집니다.
하지만 CSRF(Cross-Site Request Forgery)에 취약합니다. 공격자가 사용자의 브라우저를 이용해 강제로 요청을 보내면, 브라우저는 쿠키를 자동으로 포함하기 때문입니다. 다만 HttpOnly 옵션을 설정하면 자바스크립트에서 쿠키에 접근할 수 없어 XSS(Cross-Site Scripting) 공격에는 비교적 안전해집니다.
✅ LocalStorage, SessionStorage
LocalStorage와 SessionStorage는 브라우저 내 저장소입니다. 둘 다 서버로 자동 전송되지는 않기 때문에 토큰을 여기에 저장하면 매 요청마다 자바스크립트 코드에서 꺼내 Authorization: Bearer <token> 형태로 직접 실어 보내야 합니다.
이 방식은 요청에 자동으로 토큰 정보가 포함되지는 않기 때문에 CSRF에는 안전합니다. 하지만 JS로 접근이 가능하기 때문에 XSS 공격에 취약합니다.
참고로 여기서 두 가지의 차이점은 LocalStorage는 브라우저를 닫아도 남아 있고, SessionStorage는 탭이나 창을 닫으면 사라진다는 것입니다. 또 LocalStorage에 토큰을 두면 같은 브라우저의 모든 탭에서 공유되지만, SessionStorage는 탭마다 분리됩니다.
✅ 메모리(자바스크립트 변수)
메모리(자바스크립트 변수)에 토큰을 보관할 수도 있습니다.
이 경우 페이지 새로고침이나 브라우저를 닫으면 바로 사라지기 때문에 보안상 유출 위험은 가장 적지만, UX 측면에서 사용자가 페이지를 새로고침할 때마다 로그인이 풀리는 불편함이 있습니다.
✔️ 어디에 저장해야 할까?
이 저장 방식을 섞어 사용해 이점만 취해볼 수 있습니다.
예를 들어 Access Token은 LocalStorage나 메모리에 짧게 저장해 쓰고, Refresh Token은 HttpOnly 쿠키에 보관하는 식입니다. 이렇게 하면 토큰 탈취 위험을 줄이면서도 자동 갱신을 통해 사용자는 로그인 상태를 유지할 수 있습니다.
Access Token은 유효 기간이 짧습니다. 보통 몇 분에서 길어야 한두 시간 정도만 쓸 수 있게 발급합니다. (이 시간은 서버에서 결정합니다) 이 토큰은 클라이언트가 API를 호출할 때 Authorization 헤더에 붙여서 서버에 보냅니다. 이렇게 짧게 설정하는 이유는, 혹시 토큰이 유출되더라도 피해를 최소화하기 위해서입니다. Access Token은 브라우저의 LocalStorage나 자바스크립트 메모리에 저장하는 경우가 많습니다. 자동으로 서버에 전송되지 않기 때문에 CSRF 공격에는 안전하지만, 자바스크립트에서 접근할 수 있기 때문에 XSS 공격에는 취약합니다. 그래서 보통은 유효 기간을 짧게 두는 식으로 리스크를 줄입니다.
반면 Refresh Token은 만료 시간이 길고, 새 Access Token을 계속 발급받을 수 있는 “열쇠 같은 권한”을 갖고 있습니다. 만약 해커가 이걸 빼돌린다면, Access Token이 만료되어도 다시 Refresh Token으로 새로운 Access Token을 받아낼 수 있습니다. 결국 사용자의 계정이 장기간 완전히 털릴 수 있는 위험이 생기게 됩니다.
따라서 Refresh Token은 보통 HttpOnly 쿠키에 보관합니다. HttpOnly 쿠키는 LocalStorage나 SessionStorage처럼 자바스크립트에서 직접 접근 가능한 공간에 두는 것보다 XSS 공격에 훨씬 안전하기 때문입니다. HttpOnly 옵션이 걸린 쿠키는 자바스크립트 코드에서 읽을 수 없기 때문에, 설령 브라우저에 악성 스크립트가 심어져도 토큰을 훔쳐갈 수 없습니다. 물론 쿠키 특성상 CSRF 공격 위험이 있으므로, SameSite 옵션을 걸어두는 것이 일반적입니다.
✔️ Refresh Token 좀더 안전하게 사용하기
Refresh Token은 탈취되면 피해가 매우 크기 때문에 단순히 HttpOnly 쿠키에 넣는 것만으로는 완벽하지 않습니다. 따라서 추가적인 검증 장치가 필요합니다.
1. 환경 검증
예를 들어 서버가 Refresh Token을 검증할 때, 토큰 자체뿐 아니라 발급 당시의 IP 주소나 User-Agent 정보와 함께 비교해 일치하지 않으면 거부하는 방식입니다. 이렇게 하면 토큰이 외부로 유출되어도 다른 환경에서는 쉽게 사용할 수 없게 막을 수 있습니다.
2. 토큰 회전 (Token Rotation)
또 하나 중요한 방법은 토큰 회전(Token Rotation) 전략입니다. 클라이언트가 Refresh Token으로 새 Access Token을 발급받을 때마다 서버가 새로운 Refresh Token을 발급하고, 이전 토큰은 즉시 폐기하는 방식입니다. 이렇게 하면 공격자가 Refresh Token을 탈취해도, 사용자가 이미 새 토큰을 발급받았다면 탈취한 토큰은 무효화되어 사용할 수 없게 됩니다.

도움이 되었다면, 공감/댓글을 달아주면 깃짱에게 큰 힘이 됩니다!🌟
비밀댓글과 메일을 통해 오는 개인적인 질문은 받지 않고 있습니다. 꼭 공개댓글로 남겨주세요!