안녕하세요!
NewCodes입니다!
세션 기반 인증
이 글 하나로 끝내겠습니다!!
사전 지식으로 아래 글을 통해
인증과 인가가 무엇인지 알고 오시면 좋습니다!
읽으면서 바로 이해가 안 되는 부분이 있을 수도 있어요!
아직 잘 몰라도 괜찮아요!
처음에는 세션을 배우는 게 어렵답니다 ㅠㅠ
그래도 우선은 흐름따라 쭉 내려가면서 읽어보시는 걸 추천드려요!
이 글은 세션에 대해서만 다루고,
다음 글에서는 JWT에 대해 다룰 예정입니다!
지금 시작합니다!
🧐 세션과 JWT, 이런 거 왜 필요해?
세션과 JWT를 본격적으로 다루기 전에, 이게 왜 필요한 지 그 배경을 알아봅시다!
HTTP를 잘 들여다보면 세션이 왜 필요한지 알 수 있는데요!
웹에서는 기본적으로 HTTP를 통해 통신합니다. HTTP는 무상태성(stateless)이라는 특징을 가지고 있어요. 쉽게 말하면 HTTP는 클라이언트의 데이터를 별도로 저장하지 않는답니다.
그래서 사용자가 로그인을 통해 인증을 했어도 이 상태가 유지되지 않습니다. 사용자가 매 요청마다 로그인을 다시 해야 한다면... 이는 최악의 UX가 될 겁니다!
이러한 문제를 해결하기 위해서 세션과 JWT가 주로 쓰입니다.
그래서 한 줄로 요약하자면요!
HTTP 통신은 상태를 가지지 않기 때문에,
세션과 JWT를 이용해 별도로 인증 상태를 유지한다.
이제 세션과 JWT가 왜 필요한지는 감이 오셨을 겁니다!
이제 본격적으로 세션에 대해 파헤쳐보시죠!
1) 세션이란?
우선 '세션'이 의미하는 바가 무엇인지 알아봅시다!
세션(session)이란 '서버가 클라이언트의 상태를 일정 시간 동안 유지'하는 것을 의미합니다. 쉽게 말하면 서버에서 사용자의 데이터를 잠시 동안 저장하는 걸 의미해요.
일상생활에서 세션이라는 말을 한 번쯤 들어보셨을 텐데요. 특정 활동이 일정 시간 동안 이루어질 때, 주로 세션이라는 말을 쓰곤 합니다. 컴퓨터 과학 쪽에서의 의미와 일맥상통하죠!
2) 세션으로 인증 상태를 유지하는 원리
세션이 무엇인지는 알았고, 그러면 이를 활용해서 어떻게 세션 기반 인증을 할 수 있을까요?
먼저 쉽게 비유를 들어볼게요!
아래는 직접 그린 그림입니다 ㅎㅎ
사용자가 로그인에 성공했을 때 서버에서는 클라이언트에게 티켓을 하나 건내줍니다.
이때 서버에서는 발급한 티켓 내역을 저장하고 있습니다.
서버에서는 사용자의 티켓에 따라 사용자를 식별할 수 있습니다.
티켓을 발급한 내역과 사용자의 티켓을 비교하며
제대로 된 티켓인지 판별할 수 있죠!
또한, 어떤 사용자가 로그인되어 있는지도 알 수 있습니다.
여기까지 해서 티켓에 비유해서 세션에 대해 알아봤습니다!
이 티켓이란 게 바로 sessionId입니다!
세션 기반 인증 방식을 정리해보면 아래와 같아요!
사용자가 로그인을 했을 때 서버에서는 sessionId를 발급해서 이를 응답으로 넣어줍니다. 이때 서버에서는 발급한 sessionId 내역을 별도로 저장해둡니다. 클라이언트 측에서는 응답으로 받은 sessionId를 잘 저장해두어야 합니다.
사용자는 인증을 한 이후, 매 요청을 할 때마다 sessionId도 함께 보냅니다. 그러면 서버 측에서는 sessionId를 보고 저장해둔 sessionId 내역에 따라 어떤 사용자인지 식별할 수 있습니다. 해당 사용자가 인증된 사용자라는 게 검증되었다면, 정상적으로 사용자가 요청한 것에 따른 응답을 보냅니다.
sessionId를 활용하는 과정에 대해서는 크게 두 가지만 기억하시면 돼요!
첫 번째, 로그인할 때 서버에서는 sessionId를 발급하여 응답으로 보내준다.
두 번째, 로그인 이후 요청할 때 서버에서는 sessionId로 사용자를 검증하여 적절한 응답을 보내준다.
3) 서버에서 sessionId 발급 내역을 저장하는 방식
아까 서버에서는 sessionId를 발급하고 이 내역을 저장해둔다고 했었는데요. 이에 대해 더 자세히 알아봅시다!
서버는 각 sessionId에 따라 어떤 사용자인지 매핑을 해두어야 합니다. 그래야 사용자가 보낸 sessionId(랜덤 값)를 보고 누구인지 알 수 있기 때문이죠. 이를 위해 주로 sessionId를 key로 하고, 사용자 정보를 value로 하는 자료구조로 저장합니다.
예를 들어, 서버에서는 아래와 같이 세션 정보를 저장합니다.
sessionId | value |
d2d2d21f10bbfc9dd4e7bb1926c5e9d4 | { "userID": "13", "name": "newcodes", "email": "john@example.com", "createdAt": "2024-09-19 10:45:00", "expiresAt": "2024-09-19 12:45:00" } |
f3a5b31e21c8af2e89107df3925cbb47 | { "userID": "3", "name": "superman", "email": "alice@example.com", "createdAt": "2024-09-19 09:30:00", "expiresAt": "2024-09-19 11:30:00" } |
0a94c8f9d7ea47cb852e35bd5284c0b3 | { "userID": "7", "name": "supernova", "email": "bob@example.com", "createdAt": "2024-09-19 11:00:00", "expiresAt": "2024-09-19 13:00:00" } |
위처럼 보통 테이블 형태로 세션 정보를 저장해둡니다. 이를 통해 sessionId로 어떤 유저인지 충분히 알아낼 수 있겠죠? ㅎㅎ
4) sessionId의 두 가지 특성
sessionId는 두 가지 중요한 조건을 갖추어야 합니다.
각각의 sessionId는 고유해야 하고, 랜덤 값이 들어가야 합니다.
1. 고유해야 하는 이유는 무엇일까요?
앞서 설명했던 원리를 잘 읽으셨다면 감이 오실텐데요. sessionId로 사용자를 유일하게 식별할 수 있어야 하기 때문입니다. sessionId가 중복이 된다면, 어떤 사용자인지 판단하기 힘들겠죠.
2. 랜덤 값이 들어가야 하는 이유는 무엇일까요?
만약에 랜덤값이 아니라고 가정해봅시다. 1, 2, 3 .. 이런 식으로 sessionId를 발급해준다면 어떨까요? 만약 sessionId 탈취 시 정말 위험해집니다. 다른 user의 sessionId를 유추할 수 있기 때문입니다. 악의적인 사용자가 다른 사용자인 척 요청을 할 수 있습니다.
그렇기에 sessionId는 쉽게 예측할 수 없게 랜덤 값이 들어가야 합니다. 이러한 랜덤 값을 생성하기 위해 주로 UUID, 해시가 사용되곤 합니다.
5) sessionId를 저장하는 곳
sessionId를 발급했다면 저장을 해야겠죠!
클라이언트와 서버 둘은 각각 어디에 저장하고 있을까요?
우선 클라이언트부터 알아봅시다!
인스타그램을 웹으로 들어가서 쿠키를 살펴봤습니다. 아래 'sessionId'인 쿠키가 보이시나요?
보시다시피 클라이언트 측에서는 sessionId를 주로 쿠키에 담습니다. 네이버, 깃허브, 인프런 등등 많은 곳을 살펴봐도 쿠키에 담는 경우가 상당히 많습니다.
왜 sessionId를 쿠키에 담는 걸까요? 🧐
이유는 크게 두 가지인데요. 매 요청마다 자동으로 보내지고, 또한 보안이 좋기 때문입니다.
1. 매 요청마다 쿠키 자동 전송
: 기본적으로 브라우저는 매 요청마다 쿠키를 헤더에 자동으로 담아서 보냅니다. 그래서 클라이언트에서는 sessionId가 담긴 쿠키를 별도로 핸들링하지 않아도 됩니다.
2. 괜찮은 보안
: HttpOnly, Secure, Same-Site와 같은 쿠키의 속성을 통해 보안을 높일 수 있습니다.
- HttpOnly 속성 -> JS에서 읽을 수가 없어 *XSS 리스크 낮아진다.
- Secure 속성 -> HTTPS가 적용된 요청에만 해당 쿠키가 담아진다.
- Same-Site 속성 -> 동일한 도메인 요청에만 쿠키가 담아져서 *CSRF 리스크 낮아진다.
* XSS: Cross Site Scripting의 약자로 공격자가 웹 페이지에 악의적인 JS 코드를 삽입하여 공격
* CSRF: Cross-Site Request Forgery의 약자로 피해자의 특정 사이트 세션을 통해 악의적인 요청을 보내는 공격
위 이유는 간략하게만 다루었습니다. 우선 이 글의 목적이 세션을 이해하는 것이니 더 자세한 건 다음에 알아봅시다!
서버에서는 여러 개의 sessionId를 어디에 저장할까요?
결론부터 말씀드리자면, 세션을 저장하는 스토리지를 별도로 두어 관리하는 경우가 많습니다. 서버가 여러 대인 상황을 생각해보면 해당 방법이 가장 무난하다고 볼 수 있습니다. 이것도 자세한 건 다음에 알아봐요!
6) 세션 만료기간
세션은 영구적으로 지속되는 게 아니라 '일정 시간'동안 유지됩니다.
그렇다면 세션의 만료기간은 얼마로 두는 게 적당할까요?
이 궁금증을 해결하기 위해 여러 사이트의 세션 만료기간을 살펴봤습니다!
요약부터 하자면 이러합니다.
인프런: 7일
프로그래머스: 7일
깃허브: 14일
네이버: 한 달
깃허브나 네이버는 상대적으로 보안이 더욱 중요하니 짧을 줄 알았습니다. 그런데 그 반대여서 신기했습니다!!
그런데 생각해보면 네이버를 사용하면서 로그인을 한 달에 한 번씩 하지는 않았습니다. 깃허브도 마찬가지이고요! 깃허브를 사용하면서 2주일에 한 번씩 로그인을 했던 경험은 없었습니다. 그 이유가 무엇일까요?
세션이 만료되었을 때 이를 갱신해주는 프로세스가 있습니다!! 😮
그런데 무한정 갱신해주면 보안 상 문제가 생기겠죠. 세션을 갱신할 수 있는 조건이 있습니다!
갱신에서 중요한 조건 중의 하나가 '사용자의 활동'입니다. 사용자의 활동이 지속되지 않는데 굳이 세션을 갱신할 필요는 없습니다. 해당 웹을 사용하지 않는데도 세션을 계속 남겨두는 건 보안적으로 위험할 것이기 때문이죠.
그러면 사용자의 활동이 지속된다고 한들, 그게 정상적인 사용자의 활동이어야 갱신을 해주는 게 맞을 겁니다. 해커가 중간에 세션을 탈취해서 사용하는 거라면 갱신을 하면 안 되겠죠.
결국 정상적인 사용자의 활동을 기반으로 세션을 갱신하고, 비정상적인 접근은 방지하는 것이 핵심입니다.
7) 세션의 장단점
여기까지 오느라 수고 많으셨습니다!! 이제 마지막으로 장단점을 간략히 정리해보겠습니다!
장점
- 보안: 서버와 클라이언트 사이에 오고가는 sessionId 자체에는 사용자 정보가 들어있지 않다.
- JWT와 같은 토큰과는 달리, sessionId에는 사용자의 개인 정보가 전혀 들어가지 않습니다.
- 탈취된다고 한들 sessionId 자체로는 사용자 정보를 알아낼 수 없습니다.
- 강제 로그아웃: 탈취가 의심될 때 혹은 서비스 요구사항에 따라 서버에서 곧바로 세션을 만료시킬 수 있다.
- 서버에 세션 관련 정보를 저장해두고, 세션을 검증할 때 이를 활용하기에 가능한 일입니다.
- 이 때문에 세션 기반 인증을 서버 중심 인증 방식이라고도 합니다.
- 넷플릭스와 같이 한 계정에 대한 사용자 수가 정해져있는 요구사항에 대처하기 쉽습니다.
단점
- 서버 부하: 사용자 수가 많을수록 메모리 사용량이 늘어나고, 해야 할 입출력 횟수도 비례해서 늘어난다.
- JWT와는 달리 서버에 세션 정보를 저장해야 하기 때문에 서버에 부담이 늘어날 수밖에 없습니다.
- 확장성 문제: 서버가 여러 대일 때, 세션을 동기화할 수 있는 기술이 필요하다.
- 한 서버에서 세션을 발행했다면, 다른 서버도 알고 있어야 합니다.
- sticky session, session clustering, 세션 스토리지 분리 등의 방법이 있습니다.
- 참고) 주로 redis로 세션 스토리지 별도로 분리하는 방식을 채택합니다.
⭐️ 총정리
- 세션이란 '클라이언트의 상태를 일정 시간'동안 유지하는 것입니다.
- 세션은 stateless한 HTTP에서 상태를 유지하기 위해 쓰입니다.
- 세션 인증 방식의 과정은 크게 두 가지를 기억하시면 됩니다.
- 첫 번째, 로그인할 때 서버에서는 sessionId를 발급하여 응답으로 보내준다.
- 두 번째, 로그인 이후 요청할 때 서버에서는 sessionId로 사용자를 검증하여 적절한 응답을 보내준다.
- 클라이언트에서는 세션을 주로 쿠키에 저장합니다.
- 서버에서는 세션을 주로 key, value 형식으로 하여 별도의 스토리지에 저장합니다.
- 세션의 만료기간은 1주일 ~ 4주일로 일주일 단위로 두는 경우가 많습니다.
여기까지 해서 세션 글 마무리하겠습니다! ㅎㅎ
이 글의 목적은 세션에 대해 머릿속에 잘 들어올 수 있게 하는 거였는데요!
그렇게 되었다면 좋겠네요!!
디테일한 부분은 이제 차근차근 쌓아보시는 걸 추천드려요!
여기까지 오시느라 수고하셨습니다 😊
📚 레퍼런스
- https://stytch.com/blog/jwts-vs-sessions-which-is-right-for-you/
- https://ksj12172.tistory.com/814
- https://f-lab.kr/insight/understanding-csrf-and-xss-attacks
- https://lurgi.tistory.com/m/192
이미지 출처
- https://stytch.com/blog/jwts-vs-sessions-which-is-right-for-you/
- https://velog.io/@kingth/%EC%84%9C%EB%B2%84-%EC%9D%B8%EC%A6%9D-%EB%B0%A9%EC%8B%9D%EC%84%B8%EC%85%98%EC%BF%A0%ED%82%A4-%ED%86%A0%ED%81%B0
- https://rusyasoft.github.io/java/2019/02/15/spring-security-csrf-from-context/
- https://medium.com/sessionstack-blog/how-javascript-works-5-types-of-xss-attacks-tips-on-preventing-them-e6e28327748a
- https://namu.wiki/w/%ED%95%98%EB%8B%88%28NewJeans%29?rev=2550
- https://medium.com/@maheshlsingh8412/cookie-session-story-of-a-stateless-http-3cd09cc01541
'인증과 인가' 카테고리의 다른 글
JWT를 직접 구현하면서 토큰 인증 방식에 대해 알아보자! (0) | 2024.10.13 |
---|---|
인증과 인가, 더 이상 헷갈리지 말자! (0) | 2024.09.16 |