엘리스 소프트웨어 엔지니어 트랙을 진행하며 첫 번째 프로젝트, 쥬얼리 쇼핑몰 사이트인 usually를 완성하게 되었다. 해당 프로젝트를 진행하며 뿌듯했던 점이나 아쉬운 점, 좀 더 깊이 배우고 싶은 점 등이 많았는데 그런 요소들을 정리하고자 한다.
첫 번째 이슈, 로그인 유지 방식
이슈의 목적
첫 번째 이슈는 바로 로그인 유지 방식이었다. 쇼핑몰 서비스는 당연히 로그인을 해야 한다. 로그인을 했는데 재로딩을 할 때마다 다시 로그인을 해야 한다면 말도 안 되는 웹 사이트일 것이다! 그래서 jwt 토큰을 사용하라는 조언을 들었다.
그러니 정확히는 jwt 토큰의 사용법이 논점이었다. jwt 토큰이 정확히 무엇인지 어렴풋하게 알고 있다 보니 이번에 사용하게 되면서 제대로 파악하게 되었다. jwt 토큰에 대한 글은 따로 쓰도록 하겠다.
겪었던 이슈
jwt 토큰에 대한 의문은 우선, decoding이 생각보다 매우 쉽다는 것이었다. 그렇다면 토큰을 왜 이용하는 것인지 궁금했다. 쇼핑몰 서비스인 만큼 결제 서비스가 주요 문제가 될 것이다. 그만큼 보안이 주요 이슈여야 한다고 생각했다! 또 db에서 자동 생성한 유저 id만 토큰에 넣어 최대한 정보가 탈취당하지 않도록 고심하였다.
검색해 보니 jwt 토큰을 사용하는 방식은 다양했다.
1. 쿠키에 저장하기
2. 세션에 저장하기
3. 로컬 스토리지에 저장하기
4. 쿠키에 httponly 옵션을 사용하여 저장하기
5. 이 모든 방식에 refresh 토큰, access 토큰 등을 지정하기
개인적인 의견으로는 4번이 적절해 보였다. 그리고 당장 이주 안에 기능 구현을 해야 하니 로그인 인증을 빨리 해내고 싶었다.
이슈 해결 방법
따라서 cookie-parser라는 모듈을 설치해 httponly 옵션을 사용할 수 있도록 하였다. 방법은 간단하다! 코드로 설명하겠다.
1. 로그인을 했을 때 토큰을 발급 받고 response에 쿠키를 저장한다. userToken이 발급받은 토큰이다!
res.cookie('token', userToken, {
expires: expiryDate, //만기 기한은 따로 정해준다!
httpOnly: true,
signed: true,
});
2. 로그인이 필요한 라우터에 미들웨어를 따로 두고 그 미들웨어에서 쿠키를 읽어 온다!
const userToken = req.signedCookies.token;
3. 해당 토큰이 정상적인 토큰인지 확인한 후에, req로 넘겨 다음 function에서 쿠키를 읽을 수 있게 저장해 준다!
req.currentUserId = jwtDecoded.userId;
세션이나 local storage가 아닌 쿠키에 저장한 이유는 httponly 방식을 사용하여 단순한 js로는 쿠키에 접근 자체를 불가능하게 할 수 있기 때문이다. 직접적인 공격이 될 수 있는 크로스 사이트 스크립팅을 막는 것은 웹 사이트 보안에서 기본적이라고 생각했다.
따라서 당장 구현할 수 있는 내에서의 방식은 쿠키가 가장 적합하다고 생각했다. 또한 쿠키는 별도로 헤더에 담지 않아도 요청을 보낼 때 자동으로 담아서 보내지기 때문에 서버에서 인증을 요청해 클라이언트에서 보관해야 하는 토큰과 성격이 맞다고 생각했다. 그리고 해당 방식을 사용하면 당장 콘솔에서 document.cookie 방식으로는 쿠키를 읽어올 수 없다!
또한 이 기능을 사용하여 리프레시 토큰을 사용한다면 csrf 방식도 어느 정도 막을 수 있다고 생각했다. 공격자는 csrf 공격을 이용하여 액세스 토큰을 알 수 없기 때문이다. 다른 기본 기능들을 구현하느라 해당 방식은 아직 적용하지 못했지만 추후에 개인적으로 공부하며 적용해 볼 것이다. 물론 해커가 맘만 먹으면 또 털어버릴 수 있는 수준의 보안이지만 조금이나마 귀찮게 만들었다는 점에 의의를 둔다.
이 방식의 단점도 우선 정리해 둔다.
- 단점 : xss로부터 완전히 안전한 것은 아님, js로 request를 보내면 자동으로 요청을 위조가 가능, csrf로도 취약, url만 안다면 관련 링크를 클릭하도록 유도해 리퀘스트 위조 쉬움
또 각 해킹 방식도 간략하게 정리해 두겠다!
- XSS 크로스 사이트 스크립팅 : 공격자가 의도하는 악의적인 js 코드를 피해자 웹 브라우저에서 실행시키는 것, code injection attack
- 자바 스크립트를 누군가 우리 웹사이트에 주입한다 → httpOnly 방식을 활용하면 안전하긴 하지만 쿠키가 탈취당할 수 있음, 로컬 스토리지에 저장된 정보도 다 탈취 가능
- CSRF 공격 : 정상적인 request를 가로채 피해자인 척 하고 백엔드 서버에 변조된 request를 보내 악의적인 동작을 수행하는 공격을 의미한다. (피해자 정보 수정, 정보 열람)
이 방식을 공부하면서 jwt 토큰을 좀 더 유용하게 쓰기 위해 깊이 고민하게 되었다. 하지만 아쉬운 부분도 있다!
아쉬운 부분
1. 내가 고안한 방식에서는 프론트엔드에서 로그인 상태나 현재 로그인 한 사람이 일반 유저인지 관리자인지를 알기 어렵다. 따라서 다른 라우터를 추가해 주어야 했다.
2. refresh 토큰 방식을 꼭 사용해 보고 싶었는데 다른 기능을 구현하느라 제대로 적용해 보지 못했다. 개인적으로 포스팅을 정리하며 구현해 볼 예정이다.
3. 보안에는 끝이 없다더니 그 말이 맞았다. 이 서비스에 어느 부분이 더 중요한지, 어느 부분이 효율적일지 판단하는 것도 개발자의 능력이라는 것을 깨달았다. 공부할 게 많아지니 기쁜 건 처음이다!!
'웹 개발' 카테고리의 다른 글
맥북 MacBook hosts 파일 수정 (0) | 2023.01.13 |
---|---|
AWS3 사진 등록 (0) | 2022.07.18 |
쿠키와 세션 (0) | 2022.06.24 |
쥬얼리샵 Usually 회고글 - 두 번째 이슈 (0) | 2022.06.07 |