Node.json

쿠키와 세션

simstealer 2022. 9. 15. 11:28

쿠키

- 쿠키는 유효기간이 있으며, 키=값으로 되어있습니다.
- 서버는 미리 클라이언트에 요청자를 추정할 만한 정보를 쿠키로 만들어 보내고, 그다음 부터는 클라이언트로 부터
쿠키를 받아 요청자를 파악한다.
- 쿠키는 요청에 헤더에 담겨 전송됩니다.

// 헤더에 쿠키 심어보기
const http = require('http');

http.createServer((req, res) => 
{
	// req.headers.cookie에 쿠키가 들어 있습니다.
  console.log(req.url, req.headers.cookie);
  // 쿠키를 다음과 같이 저장하라는 의미입니다.
  res.writeHead(200, { 'Set-Cookie': 'mycookie=test' });
  res.end('Hello Cookie');
})
  .listen(8083, () => 
  {
    console.log('8083번 포트에서 서버 대기 중입니다!');
  });

결과가 다음과 같이 나올 것입니다. favicon.ico는 웹사이트 탭에 보이는 이미지를 뜻하며, 브라우저가 파비콘에 대한 정보를 알 수 없기에 서버에게 파비콘에 대한 정보를 요청하여 심어져있는 쿠키입니다.

콘솔 결과 화면

// 쿠키로 사용자를 식별할 수있는 코드
const http = require('http');
const fs = require('fs').promises;
const url = require('url');
const qs = require('querystring');

// parseCookies()함수는 문자열을 객체로 바꿔주고 있는 역할을 합니다.
// mycookie=test -> { mycookie: "test" }
const parseCookies = (cookie = '') =>
  cookie
    .split(';')
    .map(v => v.split('='))
    .reduce((acc, [k, v]) => 
    {
      acc[k.trim()] = decodeURIComponent(v);
      return acc;
    }, {});

http.createServer(async (req, res) => 
{
  // parseCookies() : 문자열을 객체로 바꿉니다.
  const cookies = parseCookies(req.headers.cookie);
  // 주소가 /login으로 시작하는 경우
  if (req.url.startsWith('/login')) 
  {
    const { query } = url.parse(req.url);
    const { name } = qs.parse(query);
    const expires = new Date();
    // 쿠키 유효 시간을 현재시간 + 5분으로 설정
    expires.setMinutes(expires.getMinutes() + 5);
    // setMinutes() : 지역시간을 임의로 설정
    // getMinutes() : 지역시간 '분'을 반환
    res.writeHead(302, { Location: '/',
      'Set-Cookie': `name=${encodeURIComponent(name)}; Expires=${expires.toGMTString()}; HttpOnly; Path=/`,
    });
    // toGMTString() : 그리니치 표준시를 사용하고 일자를 문자열로 반환합니다.
    // 쿠키명=쿠키값 : name=encodeURIComponent(name) 이걸로 씌여져 있고 헤더에는 한글을 쓸 수없기 때문에 encodeURIComponent을 사용하였다.
    // Expires=날짜(만료 기한) : 이 기간이 지나면 쿠키가 제거됩니다.
    // HttpOnly : 설정 시 자바스크립트에서 쿠키가 접근할 수 없습니다. 쿠키 조작 방지를 위해 사용합니다.
    // Path=URL : 쿠키가 전송될 URL를 특정할 수 있습니다. 기본값이 '/'입니다.
    // Secure : Https의 경우에만 쿠키가 전송됩니다.
    // Max-age=초 : Expires와 비슷하고 초로 입력할 수 있습니다.Expires보다 우선합니다.
    res.end();
  } // name이라는 쿠키가 있는 경우
  else if (cookies.name) 
  {
    res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
    res.end(`${cookies.name}님 안녕하세요`);
  } 
  else 
  {
    try 
    {
      // 쿠키가 없는 경우에는 로그인 페이지를 띄워준다.
      const data = await fs.readFile('./cookie2.html');
      res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
      res.end(data);
    } catch (err) 
    {
      res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
      res.end(err.message);
    }
  }
})
  .listen(8084, () => 
  {
    console.log('8084번 포트에서 서버 대기 중입니다!');
  });

쿠키의 정보가 노출이 되어 위험하다

세션

- 서버에 사용자 정보를 저장하고 클라이언트와는 세션 아이디로만 소통합니다.

- 세션 아이디는 꼭 쿠키를 사용해서 주고받지 않아도 됩니다.

- 세션을 위해 사용하는 쿠키를 세션쿠키라고 합니다.

// 세션을 이용 코드
const http = require('http');
const fs = require('fs').promises;
const url = require('url');
const qs = require('querystring');

const parseCookies = (cookie = '') =>
  cookie
    .split(';')
    .map(v => v.split('='))
    .reduce((acc, [k, v]) => {
      acc[k.trim()] = decodeURIComponent(v);
      return acc;
    }, {});

const session = {};

http.createServer(async (req, res) => {
  const cookies = parseCookies(req.headers.cookie);
  if (req.url.startsWith('/login')) {
    const { query } = url.parse(req.url);
    const { name } = qs.parse(query);
    const expires = new Date();
    expires.setMinutes(expires.getMinutes() + 5);
    const uniqueInt = Date.now();
    session[uniqueInt] = {
      name,
      expires,
    };
    res.writeHead(302, {
      Location: '/',
      'Set-Cookie': `session=${uniqueInt}; Expires=${expires.toGMTString()}; HttpOnly; Path=/`,
    }); // 쿠키이름을 담는 대신 세션 uniqueInt라는게 쓰였습니다. 사용자의 이름과 만료일을 uniqueInt속성안에 session이라는 객체에 저장됩니다.
    res.end();
  // 세션쿠키가 존재하고, 만료 기간이 지나지 않았다면
  } else if (cookies.session && session[cookies.session].expires > new Date()) {
    res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
    res.end(`${session[cookies.session].name}님 안녕하세요`);
  } else {
    try {
      const data = await fs.readFile('./cookie2.html');
      res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
      res.end(data);
    } catch (err) {
      res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
      res.end(err.message);
    }
  }
})
  .listen(8085, () => {
    console.log('8085번 포트에서 서버 대기 중입니다!');
  });

쿠키의 내용과 사용자 정보가 나오지 않는걸 볼 수 있습니다.

 

cookie2.html
0.00MB