-
728x90
1. JWT란?
Json Web Token
인증을 위한 정보를 특별한 저장소를 이용하지 않고, 전자 서명을 이용하여 확인하는 방법
2. JWT 구성
JWT는 웹에서 문제없이 사용할 수 있는 문자열로만 구성된 base64 인코딩을 사용
- header: 토큰의 타입, 데이터 서명 방식
- payload: 전달되는 데이터
- signature: 헤더와 페이로드의 전자서명
3. JWT 작동 방식
- 사용자 로그인 시도하면 서버는 유저 정보를 수집
- 서버는 수집한 정보와 함께 헤더와 페이로드를 결합해서 JWT로 생성하고 클라이언트에 전달
- 클라이언트는 전달받은 JWT를 이용해서 인증이 필요한 요청에 사용
4. JWT 사용하기
npm i jsonwebtoken 모듈을 설치 후 import 한다.
import jwt from "jsonwebtoken";
로그인을 성공하면 토큰을 발급받으려고 할 때,
sign() 함수로 토큰을 생성할 수 있는데 인자값은 payload(데이터)와 secret key를 넣어준다. (secret key는 .env 파일에 넣기)
payload에는 보통 고유하게 식별할 수 있는 아이디, 이메일, 역할 정보를 포함한다. 그 외 중요한 보안정보(비밀번호)는 포함 x
const token = jwt.sign( { id: user._id, isAdmin: user.isAdmin }, process.env.SECRET_KEY );
시크릿키는 아무 문자를 넣어도 되지만, 안전하게 생성하고 싶다면 터미널에 openssl rand -base64 32 입력 후 생성하기
5. JWT는 어디에 저장할까?
JWT는 개인정보라서 아무곳이나 저장하면 안되기 때문에 저장하는 곳을 크게 두가지로 나뉠 수 있는데
local storage / session storage 와 Cookie 에 저장할 수 있다.
하지만 로컬 스토리지와 세션 스토리지는 XSS(Cross-Site-Scripting) 공격에 취약해 보안 측면에서 안전하지 못하다.
그럼 쿠키에 저장하면 안전할까? HttpOnly 속성을 사용하면 XSS 공격으로 악의적인 스크립트가 쿠키를 탈취하는 걸 방지한다.
하지만 위와 같은 적절한 보안 조치를 하지 않으면 쿠키도 안전하다고 할 수 없다.
🔎 Cookie 란?
웹 서비스에서 사용되는 정보를 클라이언트에 저장하고,
HTTP 요청 시 이를 함께 전송하여 클라이언트 정보를 서버에 전달각각의 장단점이 있지만 나는 쿠키로 JWT를 저장하려고 한다.
그래서 npm i cookie-parser를 설치 후 import 해주고 미들웨어로 추가하여 쿠키를 파싱하여 사용할 수 있게 한다.
import cookieParser from "cookie-parser"; app.use(cookieParser());
이제 로그인 성공 시 토큰을 발급하고 클라이언트에게 응답을 보내야하는데
password, isAdmin을 클라이언트에게 전송하지 않기 위해 둘을 제외한 나머지 속성만 새로운 객체로 복사 후 전달한다.
"access_token"이라는 쿠키 이름을 설정 후 token 값을 할당한다.
여기서 중요한 건 {httpOnly: true}로 설정하여 생성된 쿠키가 자바스크립트에 접근할 수 없도록 한다.
const { password, isAdmin, ...otherDetails } = user._doc; res .cookie("access_token", token, { httpOnly: true }) .status(200) .json({ ...otherDetails }); } catch (err) { next(err); }
6. JWT 토큰 검증
토큰은 jwt.verify()함수로 유효성 검사하여 해독하고 검증할 수 있다.
export const verifyToken = (req, res, next) => { const token = req.cookies.access_token; if (!token) { return next(createError(401, "토큰이 없습니다.")); } jwt.verify(token, process.env.SECRET_KEY, (err, decoded) => { if (err) return next(createError(403, "유효하지 않은 토큰입니다.")); req.user = decoded; next(); }); };
요청으로부터 쿠키이름이 access_token으로 설정한 토큰값을 가져오고 만약 토큰이 없으면 에러처리를 해준다.
verify() 첫번째 인자는 검증할 토큰, 두번째 인자는 시크릿키를 전달하고 토큰이 유효하지 않은경우 에러처리를 해줬다.
토큰 검증이 오래걸리면 검증이 끝날 때 까지 다른 작업을 수행하지 못하기 때문에 비동기식으로 결과를 반환하기 위해 콜백함수를 사용해서 그에 따른 에러처리와 decoded(해독)된 데이터를 req.user에 저장한다.
req.user에 decoded된 값을 저장하는 이유는 다음 미들웨어, 핸들러에서 req.user를 사용해서 사용자 정보에 접근이 가능하다.
'Node.js' 카테고리의 다른 글
[Node.js] req.params와 req.query (0) 2023.05.27 [Node.js] bcrypt 비밀번호 암호화 하기 (0) 2023.05.25 [Node.js] Express 라우터 분리하기 (0) 2023.04.13 [Node.js] fs(파일 시스템) 사용하기 (0) 2023.04.11 [Node.js] path 모듈 (0) 2023.04.10 댓글