HTTPS : HTTP 통신을 하는 과정에서 내용을 암호화하여 데이터를 전송하는 방법
[HTTPS가 뭐고 왜 쓰나요? (Feat. 대칭키 vs. 비대칭키)]
https://www.youtube.com/watch?v=H6lpFRpyl14
mkcert라는 프로그램을 이용해서 로컬환경(내 컴퓨터)에서 신뢰할 수 있는 인증서를 만들 수 있다
brew install mkcert
mkcert install
mkcert -key-file key.pem -cert-file cert.pem localhost 127.0.0.1 ::1
//이렇게 하면 cert.pem, key.pem이라는 파일이 생성됨
생성한 인증서 파일을 HTTPS 서버에 적용해준다
express.js를 이용해서 만들어줬다
const express = require('express');
const fs = require('fs');
const https = require('https');
let server;
// 인증서 파일들이 존재하는 경우에만 https 프로토콜을 사용하는 서버를 실행합니다.
// 만약 인증서 파일이 존재하지 않는경우, http 프로토콜을 사용하는 서버를 실행합니다.
// 파일 존재여부를 확인하는 폴더는 서버 폴더의 package.json이 위치한 곳입니다.
if (fs.existsSync("./key.pem") && fs.existsSync("./cert.pem")) {
server = https
.createServer(
{
key: fs.readFileSync(__dirname + `/` + 'key.pem', 'utf-8'),
cert: fs.readFileSync(__dirname + `/` + 'cert.pem', 'utf-8'),
},
app
)
.listen(PORT);
} else {
server = app.listen(PORT)
}
쿠키란 서버에서 클라이언트에 데이터를 저장하는 방법의 하나이다
서버가 원한다면 서버는 클라이언트에서 쿠키를 이용하여 데이터를 가져올 수 있다
그런데 서버가 아무때나 이 쿠키 정보를 가져올 수 있는게 아니다
쿠키가 설정한 옵션이 충족이 되어야 서버는 클라이언트로부터 정보를 가져올 수 있다
조건들은 여러가지가 있다
1.Domain
url이
https://www.google.com/users/login
이면 도메인은 서브 도메인 정보, 세부 경로를 포함하지 않은google.com
이다. 이 도메인 정보를 충족해야 쿠키를 보내줄 수 있도록 설정할 수 있다.2.Path
https://www.google.com/users/login
여기서 Path는/users/login
이다 이 Path정보를 충족해야 쿠키를 보내줄 수 있도록 설정할 수 있다. 근데 이 Path는 설정해놓은 조건이 충족된다면 뒤에 다른 Path가 추가적으로 들어와도 상관이 없다.3.MaxAge or Expires
쿠키가 유효기간을 정하는 옵션이다.
MaxAge
는 앞으로 쿠키가몇 초
동안 유효한지 설정하고
Expires
는 앞으로 쿠키가몇 날
동안 유효한지 설정한다(Date)4.Secure
쿠키를 전송할 때 사용하는 프로토콜에 따라 쿠키 전송 여부를 결정시킬 수 있다 만약 이 값이 true면 HTTP프로토콜을 사용할 때만 전송하도록 하게 한다.
5.SameSite
요청에서 사용한 method와 아래의 해당 옵션의 조합으로 쿠키 전송 여부를 설정할 수 있다.
Lax
: Cross-Origin 요청이면 'GET'메소드에 대해서만 쿠키를 전송할 수 있다.
Strict
: Cross-Origin이 아닌 same-site인 경우에만 쿠키를 전송 할수 있다.
None
: 항상 쿠키를 보내줄 수 있다. 다만 이 옵션을 사용하기 위해서는 Secure옵션이 필요하다.
이렇게 쿠키의 옵션을 이용해서 서버는 클라이언트에 인증정보를 담은 쿠키를 전송하고, 클라이언트는 전달받은 쿠키를 서버 요청과 같이 전송하여 stateless한 인터넷 연결을 stateful하게 유지할 수 있다.
https://www.youtube.com/watch?v=OpoVuwxGRDI
쿠키에 User의 로그인 정보를 저장하면 보안상 위험이 존재하니 Server는 User id, password를 DB에 저장하고 이와 매치되는 session id를 만든 다음 User에게 반환해서 다음 로그인시 쓸 수 있도록 한다.
app.use(
session({
secret: '@codestates',
resave: false,
saveUninitialized: true,
cookie: {
domain: "localhost",
path: "/",
maxAge: 24 * 6 * 60 * 10000,
sameSite: "none",
httpOnly: true,
secure: true,
},
})
);
const { userinfo } = require(".");
const { Users } = require("../../models");
module.exports = {
post: async (req, res) => {
// userInfo는 유저정보가 데이터베이스에 존재하고, 완벽히 일치하는 경우에만 데이터가 존재합니다.
// 만약 userInfo가 NULL 혹은 빈 객체라면 전달받은 유저정보가 데이터베이스에 존재하는지 확인해 보세요.
const userInfo = await Users.findOne({
where: { userId: req.body.userId, password: req.body.password },
});
// console.log(userInfo);를 통해 userInfo가 어떤 데이터로 이루어져 있는지 확인한다. , console.log(req.session);를 통해 session이 어떻게 생겼는지 확인해 볼 수도 있다. 여기에는 cookie밖에 없는데 아래에 login이라는 key값의 객체를 하나 만들어 주고 데이터를 삽입할 것이다.
/*
Users {
dataValues: {
id: 1,
userId: 'kimcoding',
password: '1234',
email: 'kimcoding@codestates.com',
createdAt: 2020-11-18T10:00:00.000Z,
updatedAt: 2020-11-18T10:00:00.000Z
}, ...more
*/
//이렇게 나온 userInfo형태에서 password를 제외한 id, email, createdAt, updatedAt을 session객체의 login부분에 넣어준다
if (userInfo) {
// userInfo가 있는 경우
// req.session.login에 넣어야함 그리고 저장해준 다음(save), ok응답을 보내줌
req.session.login = {"userid" : userInfo.userId, "email" : userInfo.email, "createdAt" : userInfo.createdAt, "updatedAt" : userInfo.updatedAt}
req.session.save()
return res.json({"message" : "ok"}).end()
} else {
// userInfo가 없는 경우
// not authorized응답을 보내줌
return res.json({"message" : "not authorized"}).end()
}
//<참고>
//json은 메소드, 그냥 바꿔주는 기능은 없고 json형식으로 데이터를 보내겠다는 것만 말함
//JSON.stringify 는 라이브러리를 사용한거임 JSON.stringify는 JSON형태의 데이터를 문자열로 바꿔주는 역할을 함
},
};
module.exports = {
post: (req, res) => {
// 앞서 로그인시 세션 객체에 저장했던 값이 존재할 경우, 이미 로그인한 상태로 판단할 수 있습니다.
// console.log(req.session)를 통해 session이 어떤 형태의 데이터로 이루어져 있는지 확인할 수 있다.
// 1.만약 login정보가 있다면 login과 cookie객체가 두개 있을 것이고
// 2.없다면 cookie객체만 있을 것이다
// 이러한 분기에 따라 로그아웃이 가능할 때와 가능하지 않을 때를 구별해서 다른 메세지와 응답, 상태를 보내줄 수 있다.
if (!req.session.login) {
// req.session.login에 만약 로그인 정보가 없다면 로그아웃 자체가 불가능 함으로 400의 상태와 not authorized 메세지를 보내준다.
return res.status(400).json({"message" : "not authorized"}).end()
} else {
// TODO: 로그아웃 요청은 세션을 삭제하는 과정을 포함해야 합니다.
// 만약 req.session.login에 정보가 있다면 로그아웃을 해줘야 한다.
// 로그아웃이란 로그인 정보를 없애는 것임으로 session.login에 저장되어 있던 정보를 빈 객체로 만들어준다(데이터를 삭제해줌) 그리고 저장해 준다.
// 마지막으로 200의 상태와 ok라는 메세지를 응답으로 보내준다.
req.session.login = {};
req.session.save()
return res.status(200).json({"message" : "ok"}).end()
}
},
};
const { Users } = require("../../models");
module.exports = {
get: (req, res) => {
//client가 userInfo를 요청할 수 있다. 근데 userInfo(login정보)가 있는 경우가 있고 없는 경우가 있으니 이것을 구별해서 적어준다.
if (!req.session.login) {
// client가 서버에 요청을 했는데 session에 login정보 자체가 없는 경우
// 400의 상태와 not authorized메세지를 보내준다. + data가 null(비어있다)라고 알려준다
return res.status(400).json({"message" : "not authorized", data : null}).end()
} else {
// TODO: 세션 객체에 저장되어 있는 사용자 정보를 반환합니다.
// client가 서버에 요청을 했는데 session에 login정보 자체가 없는 경우
// 200의 상태와 ok메세지를 보내준다. + client가 userInfo(login정보)를 요청했음으로 data로 session에 저장되어 있는 login정보의 userid를 보내준다.
return res.status(200).json({"message" : "ok", data : {userid : req.session.login.uid}}).end()
}
},
};