Cookie / Session / Token(2)

이성은·2023년 1월 9일
0
post-thumbnail
post-custom-banner

2. Session

  • 서버가 클라이언트에 유일하고 암호화된 ID를 부여
  • 서버와 클라이언트 간 연결이 활성화 된 상태
  • 중요 데이터는 서버에서 관리

2-2. Session기반 인증

  • 세션기반 인증

    • 세션 : 사용자가 인증에 성공한 상태
    • 클라이언트에 인증 정보를 저장하지 않고, 세션 아이디를 쿠키에 담아서 클라이언트에 전송
    • 클라이언트의 중요 정보는 서버에서 관리하고, 클라이언트는 전달 받은 쿠키를 매 요청마다 서버에 제공
    • 해당 요청이 세션에 연결된 클라이언트라는 것을 서버에 알려주고, 서버는 쿠키로 전달받은 세션 아이디를 비교해서 적절하게 응답을 되돌려줌
    • 인증에 따라 리소스의 접근 권한(Authorization)이 달라짐
  • 세션기반 인증 - 로그인

    • 서버 : 사용자가 인증에 성공했음을 알고 있어야 함
    • 클라이언트 : 인증 성공을 증명할 수단을 갖고 있어야 함
    • 서버는 사용자가 인증에 성공하면 로그인 상태를 유지하기 위해 사용자의 정보를 담은 세션 객체를 생성
    • 그리고 이에 접근할 수 있는 id를 암호화하여 쿠키로 사용자에게 전달
    • 웹사이트에서 로그인을 유지하기 위한 수단으로 쿠키를 사용 → 서버에서 발급한 세션 아이디 저장
    • 서버가 "해당 유저는 인증에 성공했음"을 알고 있다면, 유저가 매번 로그인할 필요가 없음
          
  • 세션기반 인증 - 로그아웃
    • 서버 : 세션 정보를 삭제해야 함
    • 클라이언트 : 쿠키를 갱신해야 함
    • 클라이언트의 쿠키를 임의로 삭제할 수 없음
    • set-cookie 로 클라이언트에게 쿠키를 전송할 때 세션 아이디의 키값을 무효한 값으로 갱신할 수 있음

2-2. Session 단점

  • 세션은 서버의 메모리에 정보를 저장 → 주로 in-memory 또는 세션 스토어
  • 가용 메모리의 양이 줄어서 서버의 성능에 악영향
  • 기존 쿠키를 완전히 대체한 것이 아니므로 쿠키의 한계를 그대로 갖고 있음 → XSS 공격에 취약함
  • 쿠키는 세션 아이디, 즉 인증 성공에 대한 증명을 갖고 있으므로, 탈취될 경우 서버는 해당 요청이 인증된 사용자의 요청이라고 판단 → 공공 PC에서 로그아웃해야 하는 이유

Session 튜토리얼

// sever/index.js
const express = require("express");
const cors = require("cors");
const logger = require("morgan");
const session = require("express-session");
const fs = require("fs");
const https = require("https");
const controllers = require("./controllers");
const app = express();

// mkcert에서 발급한 인증서를 사용하기 위한 코드
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

const HTTPS_PORT = process.env.HTTPS_PORT || 4000;

// express-session 라이브러리를 이용해 쿠키를 설정
app.use(
	session({
		secret: "@codestates",
		resave: false,
		saveUninitialized: true,
		cookie: {
			domain: "localhost",
			path: "/",
			sameSite: "none",
			httpOnly: true,
			secure: true,
		},
	})
);
app.use(logger("dev"));
app.use(express.json());
app.use(express.urlencoded({extended: false}));

const corsOptions = {
	origin: "http://localhost:3000",
	methods: ["GET", "POST", "OPTIONS"],
	credentials: true,
};
app.use(cors(corsOptions));

app.post("/login", controllers.login);
app.post("/logout", controllers.logout);
app.get("/userinfo", controllers.userInfo);

let server;
if (fs.existsSync("./key.pem") && fs.existsSync("./cert.pem")) {
	const privateKey = fs.readFileSync(__dirname + "/key.pem", "utf8");
	const certificate = fs.readFileSync(__dirname + "/cert.pem", "utf8");
	const credentials = {
		key: privateKey,
		cert: certificate,
	};

	server = https.createServer(credentials, app);
	server.listen(HTTPS_PORT, () => console.log(`🚀 HTTPS Server is starting on ${HTTPS_PORT}`));
} else {
	server = app.listen(HTTPS_PORT, () => console.log(`🚀 HTTP Server is starting on ${HTTPS_PORT}`));
}
module.exports = server;

// server/login.js
const {USER_DATA} = require("../../db/data");
module.exports = (req, res) => {
	const {userId, password} = req.body.loginInfo;
	const {checkedKeepLogin} = req.body;
	const userInfo = {
		...USER_DATA.filter((user) => user.userId === userId && user.password === password)[0],
	};

	if (!userInfo.id) {
		res.status(401).send("Not Authorized");
	} else if (checkedKeepLogin) {
		req.session.sessionId = userInfo.id;
		req.session.cookie.maxAge = 1000 * 60 * 30;
		res.redirect("/userinfo");
	} else {
		req.session.sessionId = userInfo.id;
		res.redirect("/userinfo");
	}
};

// server/logout.js
module.exports = (req, res) => {
	const cookiesOption = {
		domain: "localhost",
		path: "/",
		httpOnly: true,
		sameSite: "none",
		secure: true,
	};
	req.session.destroy();
	res.status(205).clearCookie("connect.sid", cookiesOption).send("Logged Out Successfully");
};

// server/userInfo.js
const {USER_DATA} = require("../../db/data");

module.exports = (req, res) => {
	const sessionId = req.session.sessionId;
	const userInfo = {
		...USER_DATA.filter((user) => user.id === sessionId)[0],
	};

	if (!sessionId || !userInfo.id) {
		res.status(401).send("Not Authorized");
	} else {
		delete userInfo.password;
		res.send(userInfo);
	}
};

profile
함께 일하는 프론트엔드 개발자 이성은입니다🐥
post-custom-banner

0개의 댓글