저번에 비밀번호를 어떻게 DB에 저장하고, 암호화를 하는지 언급을 했다. 또한 토큰을 어떻게 사용하여 사용자 인증을 가지는지에 대해서 이야기를 했다. 간략하게 이야기 하자면 다음과 같다.
비밀번호는 단방향 암호화를 해서, DB가 털리더라도 그 누구도 비밀번호를 알아내지 못하도록 하여야 한다. 그를 이룰 수 있게 하는 것이 바로 해쉬 함수이다.
그러나 이 해쉬함수도 약점이 있는데, 모든 문자열에 대해서 해쉬함수값을 나열한 레인보우 테이블 공격이다. 이를 보완하기 위해서 평문에 임의의 문자열을 더해서 해쉬화 하는데 이를 Salt (Salting) 한다고 한다.
토큰은 사용자라는 것이 확인 되면 발급되고, 그 토큰은 목적이 끝나면 만료되어야한다. 만약 계속 유지가 된다면, 그 토큰이 탈취당했을 때 보안상 위험이 발생할 수 있다.
즉 토큰은 사용할 때만 발급되고 만료되어야 한다.
토큰은 사용자가 가지고 있고 그 정보는 서버가 들고 있어야 한다.
상식적으로 토큰은 어디에 넣는 것이 적합할까? 사용자의 정보는 DB에 넣는 것이 안전하다고 배웠을 것이다.
DB와 서버와 사용자간의 통신을 하면 문제점이 두가지 생긴다.
1. 오버헤드(비용)가 크다
캠프장님께서 그러셨다. 서버가 대다수 터졌다, 다운됐다의 대다수가 DB 서버의 과부하 때문이라고,, 세션에서 토큰인증방법을 사용한 것은 오버헤드를 줄이기 위해서 사용한 것인데, DB를 거치게 되면 그만큼 비효율적일 수 밖에 없다.
2. 토큰은 만료되어야한다.
즉 토큰은 만료되어야하는데, 토큰에 대한 처리를 직접 해주어야 한다.
이를 해결하기 위해서 우리는 Redis를 사용한다
레디스란 Nosql Cache 서버인데, 간단하게 말하자면 정보를 보조기억장치(SSD, Hard)가 아니라 주기억장치에 저장하는 것이다.
즉 레디스는 이러한 이유로 다음과 같은 특성을 가지게 된다.
1. 빠르다.
컴퓨터에 대해서 조금이라도 아시는 분은 주기억장치가 보조기억장치보다 훠어어어얼씬 빠르다는 것은 알것이다. 레디스는 주기억장치에 데이터를 저장하기 때문에, 그 속도는 다른 DB보다 훨씬 빠른 성능을 가지게된다.
그럼 왜 토큰을 저장하기에 최적의 DB인지 알겠는가?
오버헤드가 적을뿐더러 적절하게 휘발된다는 특성을 가지고 있기 때문에, 토큰을 관리하는데 있어 최적이 DB가 아닌가 싶다.
그렇다면 전체적인 서버의 구조를 한번 살펴보자.
(: width="300")
전체적인 서버 구조는 다음과 같다.
유저 정보 관리와 토큰 인증을 관리하는 서버를 통칭 Auth Server라고 하겠다.
사용자가 회원가입을 하면 DB 서버에 Salt와 유저정보가 각각 저장이 된다.
사용자가 로그인을 시도할 경우, Auth Server가 User DB와 SaltDB를 사용해서 사용자의 정보를 비교해보고 올바르면 JWT 토큰을, 그게 아니라면 인증을 거부한다.
JWT토큰은 사용자에게 쿠키형태로 저장되고 Redis Server에 그정보가 남는다.
P.s) 추가적으로 더하자면 AccessToken과 Refresh Token이 있으나 생략하겠다.
그다음, 사용자가 프론트 단에서 사용자의 인증이 필요한 서비스를 요구했을때, 유저의 브라우저(크롬,익스플로러,파이어폭스)에 저장되있는 Jwt 토큰값이 Redis Server에 남아있고, 그 토큰이 유효한 토큰이라면 그때서야 원하는 서비스를 제공하게 해준다는 원리이다.
비밀번호 찾기와 이메일인증또한 Redis 서버를 이용했다. (만료가 됨으로)
사용자가 비밀번호 찾기를 요구하면 사용자를 식별할수 있는 값을 redis에 저장한다
단 키값은 uuid로 완전한 랜덤생성이다.
즉 레디스는
(Key, value) = (uuid4, 사용자id) 형태로 저장이 되게 된다.
사용자가 비밀번호찾기나 이메일인증을 요구했을때 uuid를 포함한 링크를 던져주게 되는데, 그링크에 접속했을 시 비밀번호를 변경할 수 있는 권한을 가지게 된다.
서버개발캠프를 진행하면서 아키텍쳐에 대해 많은 고민을 할 수 있었다. 상황별로 어떤 프레임워크나 라이브러리를 썼을때 좀 더 효율적인 서버가 될 수 있는지에 대해 많이 고민할 수 있었던 것 같다.