Programming/Development

[Session] Session 개념 정리

Ho-home 2024. 6. 20. 22:00

현대 서비스 회사들이 많이 애용하는 세션에 관하여 설명을 드리려합니다.

JWT Token을 활용한 로그인방법도 있지만, 이번 시간에는 Session 로그인이 어떠한 과정에 의해 활용이되는지 알아보는 시간을 갖도록하겠습니다.

 

우선, 세션과 쿠키라는 두 가지의 용어에 대한 이해가 필요합니다.

 

먼저, 왜 쿠키와 세션을 사용하여야 하는가?

이에대한 답변을 내놓으려면, http 프로토콜의 특징을 먼저 알아야 하는데, 간략히

1. 비연결 지향(클라이언트가 서버에 요청했을 때 그 요청에 맞는 응답을 보낸 후 연결을 바로 끊는 형식)

2. Stateless (상태정보를 유지 안함 - 첫번 째 통신에서 데이터를 주고받았다 하여도, 두 번째 통신에서 이전 데이터를 유지하지 않음)

의 한계로 인해 사용한다고 알고있으면 되겠습니다.

 

쿠키


 

웹 서버가 생성하여 웹 브라우저로 전송하는 작은 정보 파일입니다.

웹 브라우저는 수신한 쿠키를 미리 정해진 기간 동안 또는 웹 사이트에서의 사용자 세션 기간 동안 저장합니다. 이러한 쿠키를 웹 브라우저는 향후 사용자가 웹 서버에 요청(Request)할 때 관련 쿠키를 첨부합니다.

웹 브라우저는 사용자 기기의 지정된 파일에 쿠키를 저장하는데, 예를 들어, Google Chrome 웹 브라우저는 모든 쿠키를 "Cookies"라는 파일에 저장합니다

(개발자 도구를 열고 "애플리케이션" 탭을 클릭한 다음 왼쪽 메뉴에서 "쿠키"를 클릭하면 브라우저에 저장된 쿠키를 확인할 수 있습니다.)

개발자도구 쿠키

 

이러한 쿠키는 '사용자 세션'과, '개인화', '추적'에 사용됩니다.

 

사용자 세션

쿠키는 웹 사이트 활동을 특정 사용자와 연결하는 데 도움이 됩니다.

세션 쿠키에는 사용자 세션과 해당 사용자의 관련 데이터 및 콘텐츠를 일치시키는 고유 문자열(문자와 숫자의 조합)이 포함되어 있습니다.

 

개인화

쿠키가 웹 사이트에서 사용자 행동 또는 기본 설정이 "기억" 되는 데 도움이 되므로 웹 사이트에서 사용자 경험이 맞춤화 될 수 있습니다.

 

추적

일부 쿠키에는 사용자가 방문한 웹 사이트가 기록됩니다. 이 정보는 다음에 브라우저가 해당 서버에서 콘텐츠를 로드할 때 쿠키를 생성한 서버로 전송됩니다. 예를 들면, 어느 이커머스 회사의 청바지 제품 페이지를 사용자가 보고 있다는 사실이 기록되면, 다음에 해당 추적 서비스를 사용하는 웹 사이트를 로드할 때 청바지 광고가 뜰 수 있는 형식으로 활용이 됩니다.

 

이러한 쿠키의 유형은 [ 세션 쿠키, 영구 쿠키, 인증 쿠키, 추적 쿠키, 좀비 쿠키 ]와 같이 여러종류가 있지만, 우리가 오늘 유의깊게 볼 부분은 '세션 쿠키'부분입니다.

 

세션쿠키 : 세션 쿠키는 웹 사이트에서 사용자의 세션을 추적하는 데 도움이 됩니다. 세션 쿠키는 사용자의 세션이 종료된 후, 즉 사용자가 웹 사이트에서 계정에서 로그아웃하거나 웹 사이트를 종료하면 삭제됩니다. 세션 쿠키는 만료일이 없으므로 세션이 끝나면 삭제되어야 함을 브라우저에 알립니다.

 

그렇다면 '세션'은 무엇일까요?

 

세션


 

 

세션의 개념적 정의는, 시간이 제한된 양방향 Link이며, 두 개 이상의 통신장치 또는 종단 간에 대화형 표현과 정보 교환을 가능하게 하는 TCP/IP 프로토콜의 상대적으로 높은 계층입니다.

특정 시점에 설정되고, 이후 특정 시점에 해제(종료)되는데, 상태 비저장 통신과 달리 통신하려면 통신 당사자 중 적어도 하나가 현재 상태 정보를 보유하고, 세션 기록에 대한 정보를 저장해야합니다.

또한, 서버측에서 관리되는 브라우저가 종료되기 전까지 클라이언트의 요청을 유지해줍니다.

 

세션의 작동 방식

  1. 사용자 인증 : 사용자는 서버에서 확인하는 자격 증명을 제공합니다.
  2. 세션 생성 : 인증이 성공하면 서버는 고유 식별자, 사용자 식별자, 세션 시작 시간, 만료 및 IP 주소 및 사용자 에이전트와 같은 추가 컨텍스트가 포함된 세션 기록을 생성하고, 스토리지에 저장합니다.
  3. 쿠키 저장 : 이 세션 식별자는 다시 전송되어 사용자 브라우저에 쿠키로 저장됩니다.
  4. 세션 유효성 검사 : 사용자 브라우저의 각 요청에는 이 쿠키가 포함되며, 서버는 스토리지에 쿼리하여, 세션의 유효성을 검사합니다. 유효한 경우 요청이 처리됩니다.

 

세션의 특징

  • 세션ID는 브라우저마다 한 개만 생성되어, 웹 브라우저 컨테이너에 저장되는 형식이므로, 브라우저가 종료 시 소멸됩니다. 
  • 같은 브라우저 탭에서는 새로고침을 하여도 세션ID가 유지됩니다.
  • 제한시간을 적용하여 만약 추가적인 응답이 없을 경우, session을 종료시킬 수 있습니다.
  • 비정상적인 요청이 탐지된다면, session을 핸들링하는 당사자가 session을 종료시킬 수 있습니다.

 

*원칙상 프로토콜(https)의 암호화를 믿고 가는 토큰인증방식이기에, 통신과정에서의 보안성은 세션로그인과, jwt토큰을 활용한 인증방식과는 큰 차이가 안난다고 생각합니다.[혹시, 다른 의견을 가지신 분이 계신다면 답글남겨주시면 감사하겠습니다.]

단, jwt토큰은 expire time을 설정하여 구현 하기에, 추가적인 작업이 없는 한(black list, white list와 같은 추가적 작업)은 session방식이 낫다고도 볼 수 있을것같기도 합니다.

=> session login은 토큰이 탈취되었다고 판단시, session을 만료시키면 되기때문.

 

물론 어디까지나 통신과정에서의 보안성의 경우에 그렇다는 뜻이고, 토큰 인증과정에서의 보안성은 별도일것이라는 말씀을 드리고싶네요.

 

세션인증방식의 로그인 단점

  • 서버에서 세션을 관리하여야하기때문에 클라이언트로부터 요청이 들어오면 해당 세션을 탐색하는데에 오버헤드가 발생합니다. 따라서 서버에 부하가 가해진다는 점이 있습니다.
  • 각각의 서버가 세션을 관리하기때문에, 추후 로드밸런싱과 같은 서버분산에따라 세션관련 문제가 발생할 수 있습니다. 이를 해결하기위해 'sticky session'을 채택할 수 있습니다. [sticky session? 로드 밸런서가 동일한 클라이언트에서 들어오는 요청을 식별하고 항상 해당 요청을 동일한 서버로 보낼 수 있도록 하는 방법. - 추가적으로 궁금하시다면 https://traefik.io/glossary/what-are-sticky-sessions/ 를 참고해보시길 바랍니다.]

그렇다면 session 로그인 코드는?

 

Session 로그인 예제 [express.js]


 

 

간략한 예시를 들어 Node에서 session 로그인을 구현해보겠습니다.

 

npm install express express-session

 

express를 install한 후, 세션작업을 쉽게 구현해보기 위해 'express-session'을 설치합니다.

 

//Index.js

const express = require('express');
const sessions = require('express-session');

const app = express();

app.use(
  sessions({
    secret: 'some secret',
    cookie: {
      maxAge: 1000 * 60 * 60 * 24, // 24 hours
    },
    resave: true,
    saveUninitialized: false,
  })
);

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.listen(3000, () => {
  console.log(`Server Running at port 3000`);
});

 

index.js의 코드 구성입니다.

 

여기서 유의깊게볼 점들이 있습니다.

  • secret : 세션 ID 쿠키에 서명하는데 필요합니다. 추측할 수 없는 값을 사용하면 하이재킹 어택에 대한 가능성이 줄어듭니다.
  • cookie : 세션 ID 쿠키에대한 구성이 포함된 객체입니다.
  • resave : 요청 중에 세션 데이터가 수정되지 않은 경우에도 세션을 세션 저장소에 다시 저장하도록 합니다.
  • saveUninitialized: "초기화되지 않은" 세션을 강제로 저장소에 저장합니다. 즉, 세션이 시작되지 않은 경우에도 세션을 저장소에 저장합니다.

이후, 'HomeHandler'라는 파일을 생성합니다.

 

//Home.js

module.exports = function HomeHandler(req, res) {
  if (!req.session.userid) {
    return res.redirect('/login'); // req.session.userid가 없다면 /login으로 redirect
  }

  res.setHeader('Content-Type', 'text/HTML');
  res.write(`
    <h1>Welcome back ${req.session.userid}</h1>
    <a href="/logout">Logout</a>
  `);

  res.end();
};

 

이 후, 이 모듈에대한 선언을 index.js에서 해줍니다.

 

//Index.js

const HomeHandler = require('./Home.js');

app.get('/', HomeHandler);

 

그리고, login 페이지를 간단히 구현합니다.

 

//Login.js

module.exports = function LoginHandler(req, res) {
  if (req.session.userid) {
    return res.redirect('/');
  }

  res.setHeader('Content-Type', 'text/HTML');
  res.write(`
    <h1>Login</h1>
    <form method="post" action="/process-login">
      <input type="text" name="username" placeholder="Username" /> <br>
      <input type="password" name="password" placeholder="Password" /> <br>
      <button type="submit">Login</button>
    </form>
  `);

  res.end();
};

 

이후, 다시 Index.js에서 login.js를 선언해줍니다.

//Index.js

const LoginHandler = require('./Login.js');

app.get('/login', LoginHandler);

 

다음으로, process-login 모듈을 만들어줍니다.

module.exports = function processLogin(req, res) {
  if (req.body.username !== 'admin' || req.body.password !== 'admin') {
    return res.send('Invalid username or password');
  }

  req.session.userid = req.body.username;

  res.redirect('/');
}

 

* username과 password가 'admin'이라면 request.session.userid를 'admin'으로 세팅해준 후, '/' 경로로 redirect 해줍니다.

그렇지 않다면, 'Invalid username or password' 를 반환합니다.

 

이후, 이 또한 Index.js에 선언해줍니다.

 

//Index.js

const ProcessLoginHandler = require('./handlers/process-login.js');

app.post('/process-login', ProcessLoginHandler);

 

여기까지 하면 login에 대한 구현은 끝났고, log-out을 구현하자면,

 

//Logout.js

module.exports = function Logout(req, res) {
  req.session.destroy();
  res.redirect('/');
};

//Index.js

const LogoutHandler = require('./Logout.js');

app.get('/logout', LogoutHandler);

 

Logout.js와 Index.js에 선언을 각각 해주면 완성입니다.

이후, 터미널창에서 'npm run start'를 타이핑하고, localhost:3000으로 진입하면 간략한 session방식의 로그인, 로그아웃과정을 체험해볼 수 있습니다.

 

후기


 

제 개인적인 의견으로는, Jwt와, Session login방식은 어느방식이 더 좋은것보다는 상황에따라, 필요에따라 선택하는것이 중요한것같습니다. 단, 각각의 방식에따라 어떻게 작동이되는지를 이해하고, 추가적인 로직을 구현하여 적절한 보안성을 챙기는것이 중요한것같습니다. 이상입니다.

 

 

[출처]

 

Authentication: JWT usage vs session

What is the advantage of using JWTs over sessions in situations like authentication? Is it used as a standalone approach or is it used in the session?

stackoverflow.com

 

Session Based Authentication - roadmap.sh

Learn what is Session Based Authentication and how to implement it in Node.js

roadmap.sh

 

What Are Sticky Sessions | Traefik Labs

Thanks to sticky sessions, applications remember user preferences, keep users authenticated, etc. But how do they work exactly, and when should you use them?

traefik.io