[2024.05.21 TIL] 내일배움캠프 25일차 (쿠키와 세션, JWT)

My_Code·2024년 5월 24일
0

TIL

목록 보기
33/113
post-thumbnail

본 내용은 내일배움캠프에서 활동한 내용을 기록한 글입니다.


💻 TIL(Today I Learned)

📌 Today I Done

  • 브라우저가 서버로부터 응답으로 Set-Cookie 헤더를 받은 경우 해당 데이터를 저장한 뒤 모든 요청에 포함하여 보냄

  • 쿠키는 사용자가 naver.com과 같은 웹 사이트를 방문할 때마다 이전에 방문했던 정보를 기억하는 데이터 파일

  • 데이터를 여러 사이트에 공유할 수 있기 때문에 보안에 취약할 수 있음


✏️ 세션 (Session)

  • 쿠키를 기반으로 구성된 기술

  • 클라이언트가 마음대로 데이터를 확인 할 수 있던 쿠키와는 다르게 세션은 데이터를 서버에만 저장

  • 일반적으로 세션 Id를 쿠키를 이용해 클라이언트에게 전달하여, 서버는 이 세션 Id를 사용해 저장된 세션 데이터를 조회

  • 보안성은 좋으나, 반대로 사용자가 많은 경우 서버에 저장해야 할 데이터가 많아져서 서버 컴퓨터가 감당하지 못하는 문제가 생기기 쉬움


✏️ 쿠키 만들어보기

  • Set-Cookie 를 이용하여 쿠키 할당하기
// 'Set-Cookie'를 이용하여 쿠키를 할당하는 API
app.get("/set-cookie", (req, res) => {
	let expire = new Date();
	expire.setMinutes(expire.getMinutes() + 60); // 만료 시간을 60분으로 설정

	res.writeHead(200, {
		'Set-Cookie': `name=sparta; Expires=${expire.toGMTString()}; HttpOnly; Path=/`,
	});
	return res.end();
});
  • res.cookie()를 이용하여 쿠키 할당하기
// 'res.cookie()'를 이용하여 쿠키를 할당하는 API
app.get("/set-cookie", (req, res) => {
  	let expires = new Date();
  	expires.setMinutes(expires.getMinutes() + 60); // 만료 시간을 60분으로 설정

  	res.cookie('name', 'sparta', {
    	expires: expires
  	});
  	return res.end();
});


✏️ req를 이용하여 쿠키 접근하기

  • req.headers.cookie를 이용하여 쿠키 조회하기
// 'req.headers.cookie'를 이용하여 클라이언트의 모든 쿠키를 조회하는 API
app.get('/get-cookie', (req, res) => {
  	const cookie = req.headers.cookie;
  	console.log(cookie); // name=sparta
	return res.status(200).json({ cookie });
});

  • cookie-parser 설치하기
# yarn을 이용해 cookie-parser를 설치합니다.
yarn add cookie-parser
  • cookie-parser 미들웨어를 전역으로 사용
app.use(cookieParser());
  • cookie-parser 등록하기, app.js
import cookieParser from 'cookie-parser';

app.use(cookieParser());

// 'req.cookies'를 이용하여 클라이언트의 모든 쿠키를 조회하는 API
app.get('/get-cookie', (req, res) => {
  	const cookie = req.cookies;
  	console.log(cookie);
  	return res.status(200).json({ cookie });
});

✏️ 세션(Session) 만들어보기

  • 쿠키의 경우 서버를 재시작하거나 새로고침을 하더라도 로그인이 유지됨

  • 사용자에게는 편리하지만, 서버의 입장에서는 보안 문제가 발생할 수 있음

  • 그렇기에 쿠키에는 사용자가 누구인지 확실하게 구분할 수 있는 정보를 넣어주어야 할

  • 민감한 정보는 서버에서만 관리하고, 사용자 식별 정보를 통해 사용자의 정보를 반환

  • /set-session API 만들기

let session = {};
app.get('/set-session', function (req, res, next) {
  	// 현재는 sparta라는 이름으로 저장하지만, 나중에는 복잡한 사용자의 정보로 변경될 수 있음 
  	// ID, IP, user-agent 등등
  	const name = 'sparta';
  	const uniqueInt = Date.now();
  	// 세션에 사용자의 시간 정보 저장
  	// 시간 정보를 세션 ID로 사용
  	session[uniqueInt] = { name };

  	res.cookie('sessionKey', uniqueInt);
  	return res.status(200).end();
});
  • /set-session API 만들기
app.get('/get-session', function (req, res, next) {
  	const { sessionKey } = req.cookies;
  	// 클라이언트의 쿠키에 저장된 세션 키(ID)로 서버의 세션 정보를 조회
  	const name = session[sessionKey];
  	return res.status(200).json({ name });
});
  • 즉, 서버에 접근하기 위한 세션ID를 쿠키에 저장해서 그 세션ID를 통해서 서버에 있는 데이터에 접근함

✏️ JWT란?

  • JWT(Json Web Token)은 웹 표준으로써, 서버와 클라이언트 사이에서 정보를 안전하게 전송하기 위해 도움을 주는 웹 토큰(Web Token)

  • 다양한 암호화 알고리즘을 사용할 수 있어, 신뢰성을 보장함

  • 헤더, 페이로드, 시그니처로 구성됨

    • 헤더: 토큰 종류와 해시 알고리즘 정보가 들어있음
    • 페이로드: 토큰의 내용물이 인코딩된 부분
    • 시그니처: 일련의 문자열로, 시그니처를 통해 토큰이 변조되었는지 여부 확인
    • 시그니처는 JWT 비밀키로 만들어지고, 비밀키가 노출되면 토큰 위조 가능

✏️ JWT의 특성 정리하기

  • JWT는 비밀 키를 모르더라도 복호화(Decode)가 가능

  • 변조만 불가능 할 뿐, 누구나 복호화하여 보는것은 가능하다는 의미

  • 민감한 정보(개인정보, 비밀번호 등)는 담지 않도록 해야함


✏️ JWT vs 쿠키/세션

  • 데이터를 교환하고 관리하는 방식인 쿠키/세션과 달리, JWT는 단순히 데이터를 표현하는 형식

  • JWT로 만든 데이터는 변조가 어렵고, 서버에 별도의 상태 정보를 저장하지 않기 때문에, 서버를 Stateless(무상태)로 관리할 수 있음

  • 쿠키와 세션은 사용자의 로그인 정보나 세션 데이터를 서버에 저장하므로 상태를 유지하기 때문에 Stateful(상태 보존)하게 데이터가 관리됨

  • Node.js 서버가 언제든 죽었다 살아나도 똑같은 동작을 하면 Stateless

  • 서버가 죽었다 살아났을때 조금이라도 동작이 다른 경우 Stateful


✏️ JWT로 JSON 데이터 암호화

  • jsonwebtoken 라이브러리의 sign 메서드를 사용해 JWT를 생성
import jwt from 'jsonwebtoken';

const token = jwt.sign({ myPayloadData: 1234 }, 'mysecretkey');
console.log(token); // eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJteVBheWxvYWREYXRhIjoxMjM0LCJpYXQiOjE2OTA4NzM4ODV9.YUmYY9aef9HOO8f2d6Umh2gtWRXJjDkzjm5FPhsQEA0

✏️ JWT로 JSON 데이터 복호화

  • jsonwebtoken 라이브러리의 decode 메서드를 사용해 JWT를 복호화
import jwt from 'jsonwebtoken';

const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJteVBheWxvYWREYXRhIjoxMjM0LCJpYXQiOjE2OTA4NzM4ODV9.YUmYY9aef9HOO8f2d6Umh2gtWRXJjDkzjm5FPhsQEA0";
const decodedValue = jwt.decode(token);

console.log(decodedValue); // { myPayloadData: 1234, iat: 1690873885 }

✏️ JWT로 변조되지 않은 데이터인지 검증

  • jsonwebtoken 라이브러리의 verify 메서드를 사용해 JWT를 검증
import jwt from 'jsonwebtoken';

const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJteVBheWxvYWREYXRhIjoxMjM0LCJpYXQiOjE2OTA4NzM4ODV9.YUmYY9aef9HOO8f2d6Umh2gtWRXJjDkzjm5FPhsQEA0";
const decodedValueByVerify = jwt.verify(token, "mysecretkey");

console.log(decodedValueByVerify); 
// 검증에 성공할 경우 => { myPayloadData: 1234, iat: 1690873885 }

// 검증에 실패할 경우 => JsonWebTokenError: invalid signature

✏️ JWT를 사용하는 이유

  • JWT가 인증 서버에서 발급되었는지 위변조 여부를 확인할 수 있음

  • 누구든지 JWT 내부에 들어있는 정보를 확인할 수 있음 (복호화)


✏️ JWT를 적용하지 않은 로그인 API

  • 사용자의 정보가 sparta 이름을 가진 쿠키에 할당

  • 쿠키의 속성값이나 만료 시간을 클라이언트가 언제든지 수정할 수 있음

  • 쿠키의 위변조 여부를 확인 할 수 없음

import express from 'express';
const app = express();

app.post('/login', function (req, res, next) {
  	const user = { // 사용자 정보
    	userId: 203, // 사용자의 고유 아이디 (Primary key)
    	email: "archepro84@gmail.com", // 사용자의 이메일
    	name: "이용우", // 사용자의 이름
  	}

  	res.cookie('sparta', user);  // sparta 라는 이름을 가진 쿠키에 user 객체를 할당
  	return res.status(200).end();
});

app.listen(5002, () => {
  	console.log(5002, "번호로 서버가 켜졌어요!");
});

✏️ JWT를 적용한 로그인 API

  • 사용자의 정보를 Payload에 저장한 JWT를 sparta 이름을 가진 쿠키에 할당

  • JWT를 생성할 때 위변조 여부를 확인할 수 있는 비밀키를 사용

  • 쿠키의 만료시간과 별개로 JWT의 만료시간을 설정

import express from 'express';
import JWT from 'jsonwebtoken';

const app = express();

app.post('/login', (req, res) => {
  	// 사용자 정보
  	const user = {
    	userId: 203,
    	email: 'archepro84@gmail.com',
    	name: '이용우',
  	};

  	// 사용자 정보를 JWT로 생성
  	const userJWT = JWT.sign(
    	user, // user 변수의 데이터를 payload에 할당
    	'secretOrPrivateKey', // JWT의 비밀키를 secretOrPrivateKey라는 문자열로 할당
    	{ expiresIn: '1h' }, // JWT의 인증 만료시간을 1시간으로 설정
  	);

  	// userJWT 변수를 sparta 라는 이름을 가진 쿠키에 Bearer 토큰 형식으로 할당
  	res.cookie('sparta', `Bearer ${userJWT}`);
  	return res.status(200).end();
});

app.listen(5002, () => {
  	console.log(5002, '번호로 서버가 켜졌어요!');
});

📌 Tomorrow's Goal

✏️ 노드 숙련 강의 시청하기

  • 2주차 남은 강의를 시청

  • 최대한 시청 후 과제 문서를 작성할 수 있도록 노력할 것!!

  • 내일도 시간이 많지 않기 때문에 오전, 오후에 최대한 시청할 예정



📌 Today's Goal I Done

✔️ 노드 숙련 강의 시청하기

  • 예비군으로 밀린 강의를 시청함

  • 원래는 2주차를 오늘 안에 다 시청하려고 했으나, 생각보다 내용이 어려웠음

  • 완전히 막힌다기 보다는 내용이 어려워서 검색하면서 이해하느라 진행이 느렸음

  • 현재 2주차 1/3 정도 완료한 상태

  • 주말을 이용해서 최대한 강의를 들을 예정


profile
조금씩 정리하자!!!

0개의 댓글