세션이란 두 호스트 간에 일정 시간 동안 유지되는 논리적인 연결 상태를 말한다.
세션에 대해 자세히 알기 위해선
먼저 OSI 7계층과 TCP/IP 5계층에 대해 알아야 한다.
학교에서 컴퓨터 네트워크를 배울 땐 보통 OSI 7계층을 메인으로 다뤘었다.
하지만 꼭 TCP/IP 5계층도 같이 옆에 있었는데,
하나만 있으면 되지 왜 2개가 있는걸까? 그리고 이 둘의 차이는 뭘까?
결론부터 말하자면 대부분의 운영체제는 TCP/IP 모델을 기준으로
네트워크 스택이 구현되어 있다.
그렇다면 왜 이론을 배울 땐 OSI 모델을 기준으로 배울까.
이는 OSI 모델이 각 계층의 기능을 더 세세히 구분지어,
네트워크 개념을 설명하는데에 더 수월하다고 생각했기 때문이다.
근데 나는 오히려 2개가 있으니까 더 헷갈리는 느낌이라
TCP/IP 5계층 기준으로 설명 하는게 더 나았지 않았을까 싶다.
두 모델이 공존하게된 역사적 배경
네트워크 통신 표준화 과정에서 OSI 모델과 TCP/IP 모델이 경쟁하며 발전했다.
다만 TCP/IP는 이미 실제 동작하는 구현체가 있었던 반면,
OSI 모델은 복잡성으로 인해 실제 구현이 어려운 이론적 모델에 가까웠다.
결과적으로 TCP/IP가 실용성과 구현 용이성으로 먼저 널리 채택되었다.
1980년대에 BSD UNIX를 비롯한 많은 시스템에서 TCP/IP를 채택하면서 이러한 추세가 공고해졌다.
특히 인터넷의 급속한 성장과 함께 TCP/IP는 네트워크 통신의 사실상 표준으로 자리잡게 되었다.
어쨌든 실제 커널들에는 TCP/IP 모델 기준으로 구현되어 있으니
이 기준으로 생각하면 된다.
따라서 OSI 모델에서 별도의 계층으로 존재하던 세션 계층은
실제적으론 응용계층 즉, 사용자 단에서 구현해줘야 한다.
나는 세션 계층이 따로 존재하고 우리는 응용계층의 일만 하면 된다고 생각해서
왜 세션을 개발자가 직접 처리하는걸까? 라는 의문이 있었는데
이러한 이유 때문에 세션을 직접 처리하는 것이었다.
여기까지가 세션을 왜 직접 구현해야 하는지에 대한 배경 설명이었다.
이제 진짜 세션에 대해 알아보자.
서두에서도 말했듯
세션은 두 호스트 간의 논리적인 연결이다.
서버는 세션 객체를 통해 클라이언트의 상태 정보를 저장한다.
클라이언트는 서버로부터 받은 세션 ID로 다음 요청 시 자신을 알린다.
그렇다면 여기서 논리적인 연결이라는건 뭘까?
"논리적인 연결"은 물리적으로 연결되어 있지 않아도,
서버와 클라이언트가 서로를 인식하고 지속적인 상호작용을 할 수 있게 해주는
개념적인 연결 상태를 의미한다.
근데 TCP에서도 호스트간의 연결을 맺는데
이 둘간에는 어떤 차이가 있을까
TCP의 connection을 물리적인 연결이라고 설명하기도 하는데,
실제 물리적인 연결은 유선 연결과 무선 연결을 뜻하고,
TCP도 논리적으로 연결 상태만 나타내므로
엄밀히는 논리적인 연결이다.
결국 connection도 논리적으로 연결된 상태를 나타내는 것인데
세션과 어떤 차이가 있는걸까?
이 둘은 사실 돌아가는 형태만 비슷하고
아예 다른 녀석들이다.
세션과 TCP의 connection은 서로 다른 계층에서 독립적으로 관리된다.
세션은 응용 계층에서 관리하며 connection은 전송계층에서 관리된다.
이해하기 쉽게 예를 들어보자면
여기 세션을 관리하는 철수와 TCP 연결을 관리하는 영희가 있다.
이 둘은 다른 부서(계층)에 있어서 서로의 존재조차 모른다.
철수는 그저 자기 부서에서 이 클라이언트에 대해
세션 객체를 생성하여 정보를 관리하다가,
클라이언트가 세션 ID를 들고오면
이를 검증하고 대응되는 세션 객체를 가져와
클라이언트의 정보를 가져올 뿐이다.
철수는 영희에 대해 모른다.
반대로 영희도 그저 클라이언트가 왔을 때
SYN과 ACK를 주고 받으며 잘 마무리가 되면
connection 객체를 생성해서 저장할 뿐이다.
이처럼 둘 다 논리적인 연결로 비슷해 보일 수 있지만
둘이 처리되는 위치와 내용은 완전히 별개이다.
각 계층은 서로 독립적이며 다른 계층에 대해 알지도 알 필요도 없다.
각 계층은 그저 자기가 할 일만 하고 다음 계층으로 넘겨주기만 하면 된다.
전송 계층은 connection에 대한 처리를 하고 넘길 뿐이고,
응용 계층은 세션에 대한 처리를 할 뿐이다.
이런 의문이 들 수 있다.
"둘이 서로 별개라면, 왜 그냥 이 둘의 작업을 합쳐서 한번에 처리하지 않을까?"
여기에는 그럴만한 이유가 있다.
우선, 이 두 연결의 목적이 서로 다르다.
세션은 응용 계층에서 이루어지며,
애플리케이션 수준에서 사용자가 누구인지 확인하고,
사용자의 인증 정보를 비교적 오랜 시간 유지하는 역할을 한다.
반면, TCP 연결은 더 낮은 계층에서 이루어지며,
전송할 패킷 데이터가 목적지에 제대로 도착하는지 보장하기 위한 연결을 수행한다.
여기서는 사용자가 철수인지 영희인지 중요하지 않고,
내가 보내려는 데이터가 "지정한 컴퓨터로 정확히 도착하는지"가 중요하다.
이 컴퓨터의 주인이 누구인지는 관심 밖의 문제다.
반면, 세션에서는 패킷의 전송이 잘 이루어지는 것을 전제로 하여,
통신하는 상대방이 "누구인지" 확인하는 것이 주요 관심사이다.
그렇다면 세션은 어떻게 사용자를 식별할까.
클라이언트가 요청을 보내면 서버는 무작위로 Session ID를 생성한다.
서버는 세션 아이디와 함께 사용자의 로그인 상태, 장바구니 정보 등과 같은 데이터를 저장한다.
이러한 세션 데이터는 보통 서버의 메모리나 디비에 저장된다.
이렇게 생성한 세션 아이디는 보통 쿠키를 통해 클라이언트에게 전달된다.
HTTP 응답 메시지에 Set-Cookie
헤더를 사용하여 보내면
클라이언트의 부라우저가 이 쿠키를 저장하고,
이후 요청을 보낼 때마다 브라우저가 쿠키에 저장된 세션 아이디를 자동으로 포함시킨다.
HTTP/1.1 200 OK
Set-Cookie: sessionid=abc123; Path=/; HttpOnly
보다시피 세션아이디는 응답 메시지에 그대로 노출되기 때문에
보안을 위해 Secure
와 같은 설정을 하여 HTTPS에만 쿠키가 전송되게 하는게 좋다.
또한 HttpOnly
속성을 설정하면,
자바스크립트와 같은 클라이언트 측 스크립트가
쿠키에 접근하는 것을 막을 수 있다.
이로써 XSS(Cross-Site Scripting) 공격으로부터
세션 ID를 보호할 수 있다.
물론 브라우저는 여전히 쿠키를 통해 세션 ID를 정상적으로 처리할 수 있다.