우리가 알고 있는 HTTP통신은 stateless하다. 이 뜻은 서버는 응답만하는 기계일 뿐이지, 요청자가 누구인지 기억하지 않는다는 뜻이다.
API서버를 만들다 보면 개인정보에 관한 내용들은 누구에게 함부로 응답을 줘서는 안된다. 인증된 클라이언트에게만 정보를 전달해야한다.
그런데 서버는 클라이언트가 누구인지 모른다고 했다. 그렇다면 클라이언트가 서버에게 나는 누구야! 라고 알려줘야 할텐데 어떻게 진행하면 좋을까?
그런데 여기서 서버는 "인증된 사용자" 라는 것을 어떤 방식으로 처리할까?
세션 로그인이란 서버에서 {"session ID" : "value"}
값을 생성하여 메모리상에 저장해두고, session ID
값을 쿠키로 만들어 클라이언트에게 전달한다.
그런 뒤에 클라이언트가 session ID
값을 쿠키로 전달해주면 해당 session ID
값에 해당하는 value
를 조회해 해당 유저의 정보를 알려주게 된다.
{"session ID" : "value"}
값을 생성하여 관리하게 된다.세션 로그인은 "서버가 클라이언트 상태 정보"를 갖고 관리 및 인증을 하는 반면 JWT Token은 신경쓰지 않는다.
처음 JWT Token을 발급한 뒤부터는 오로지 해당 토큰이 유효한지 유효하지 않은지만 판단하여 인증을 진행한다.
그렇기 때문에 서버에서는 발급하는 비용, 유효한지 판단하는 비용만 존재한다.
서버에서 어떤 정보를 저장할 필요가 없다.
토큰의 타입과 해싱 알고리즘의 종류를 저장한다.
base64url로 인코딩된다.
JWT Token은 base64가 아닌 base64url이라는 것으로 인코딩된다고 한다.
이유를 찾아보니 예전에 url로 jwt가 전달되었었는데 이 부분이 표준이 되서 지금까지도 쓰이는 것 같다.
https://stackoverflow.com/questions/56711129/why-do-you-use-base64-url-encoding-with-json-web-tokens
토큰에 담을 정보가 들어간다.
무엇이든 들어갈 수 있지만 암호화되어있는 것이 아니여서 중요한 정보는 담으면 안된다.
일반적으로 토큰의 발급시간, 발급자, 사용자 아이디값등이 들어간다.
base64url로 인코딩한다.
이 부분이 JWT Token이 유효한지 아닌지 판단할 수 있는 부분이다.
Header + Payload를 합친뒤, 서버에서 설정한 Secret Key를 이용하여 Header에 나타난 해싱 알고리즘으로 암호화를 한다.
그 다음 base64url로 인코딩한다.
인증하는 부분은 Signature을 복호화하여 넘어온 Header, Payload와 값이 일치하는지 아닌지를 판단하여 인증을 진행한다.
그렇게 {Header}.{Payload}.{Signature}
형식으로 JWT Token이 완성된다.
클라이언트는 Session이나 JWT Token 어느것이든 이용한다고 한들, 인증 정보를 갖고 있어야 한다.
이 인증 정보들을 어디에다가 저장해놔야 안전할까?
진짜 이 부분은 며칠동안 고민하고 찾아봤다. 하지만 보안이라는 것이 100% 완전 보안! 이라는 말은 존재하지 않는다. 최대한 막아보자. 라는 것이 우리의 목표일 뿐이다.
그래서 사람들마다 생각이 다르다. 이 부분도 결국 나의 생각일 뿐, 이걸로 해야한다! 라는 뜻은 아니다.
LocalStorage는 너무 쉽게 접근이 가능하다. XSS공격도 쉽게 당할 수 있다.
그래서 보안적으로 유리해보이지 않는다.
즉, Cookie를 사용하는 것이 옳다는 의견이다.
HttpOnly옵션을 주면 브라우저에서 코드상으로 접근이 불가능하게 막을 수 있다.
Secure옵션을 주면 Https통신에서만 쿠키가 전달되도록 하여 보안을 높일 수 있다.
SameSite 옵션을 통해 같은 도메인상에서만 api호출이 가능하도록 만들 수 있다.
이 부분도 궁금했다. 위에서 말했다싶이 세션과 JWT는 서버에서 클라이언트 정보를 관리하냐, 안하냐의 차이이다.
세션이든 JWT Token이든 클라이언트 상에 정보를 저장한다는 것은 동일하다.
그러므로, 탈취당할 위험성도 동일하다.
하지만, 세션은 클라이언트가 "나 해킹당했어!" 라고 서버에게 말해주면 서버는 해당 세션 id값을 파기하고 새로 발급해주는 식으로 어느정도 대응이 가능하다.
JWT는 "나 해킹당했어!" 해도 어떻게 대응할 방도가 존재하지 않는다. Signature의 해싱방식을 바꾸든, secret key를 바꿔서 해결 할 수 밖에 없다.
그렇게 되면 모든 사용자가 다시 인증을 받아야 하니 좋은 대응 방안은 아닌 것 같다.
세션과 JWT의 차이는 메모리를 잡아먹느냐, 개개인의 사용자를 제어할 수 있느냐. 의 차이인 것 같다.
결론은 현재 프로젝트에 어떤 성향이 더 맞는지를 판단하여 적용하는 것이 옳다. 더 좋다 라는 개념은 없다.
참고
https://millo-l.github.io/Session-기반-인증방식
https://github.com/boojongmin/memo/issues/7
https://ledgku.tistory.com/72
https://velopert.com/2389
https://velog.io/@0307kwon/JWT는-어디에-저장해야할까-localStorage-vs-cookie