DB
[ETC] 로그인 방식의 변화와 암호화
로그인 방식의 변화
1단계: 세션
브라우저에서 이메일과 비밀번호로 로그인하면 백엔드로 로그인 API 요청이 가고, 백엔드는 DB에서 해당 유저를 확인한 뒤 세션에 저장합니다.
그다음 세션 id를 브라우저로 보내 줍니다.
브라우저는 이후 요청마다 이 id를 함께 보내서 자신이 누구인지 식별할 수 있게 합니다.

문제점
- 백엔드 서버가 꺼지면 로그인 정보가 사라져 다시 로그인해야 합니다.
- 모든 유저의 세션을 서버 메모리에 보관하기 때문에 동시에 많은 유저를 처리하기에 한계가 있습니다.
해결방법 Scale-up: 서버 한 대의 성능(메모리 등)을 높이는 방법
2단계: 서버 여러 대로 분산
서버 성능을 높여도 동시 접속자가 더 많아지면 여전히 서버에 부하가 생깁니다.
그래서 나온 방법이 백엔드 서버를 여러 대로 늘리는 것입니다.
같은 백엔드 서버를 여러 대 두고 부하를 분산합니다.

해결방법 Scale-out: 서버 한 대 대신 여러 대를 사용해 트래픽을 분산하는 방법
- 문제점: 1번 서버에서 로그인했는데 다음 요청이 2번 서버로 가면, 2번 서버에는 세션이 없어 인증되지 않습니다. (서버가 상태 정보를 가지고 있는 방식을 stateful이라고 합니다.) (서버가 상태 정보를 가지지 않는 방식을 stateless라고 합니다.)
- 모든 서버가 하나의 DB를 쓰기 때문에 병목 현상이 생깁니다.
3단계: 로그인 정보를 DB와 Redis에 저장
세션을 서버 간에 공유하지 못하는 문제를 보완하고, DB에 몰리는 부하를 줄이기 위한 방법입니다.
세션을 공유하지 못하자 로그인 정보를 DB에 저장하기 시작했습니다.
하지만 이 방법은 서버의 부하를 DB로 옮긴 것이라 결국 DB에 부하가 생깁니다.
DB를 복제하는 방법도 있지만 비용이 많이 들어 비효율적입니다.

테이블을 세로로 나누면 수직 파티셔닝, 가로로 나누면 수평 파티셔닝(샤딩)이라고 합니다. 나눈 데이터를 각각의 DB에 저장해 병목 현상을 줄였습니다.
즉, 데이터를 나눠서 문제를 해결합니다.

해결방법 Redis: 메모리에 데이터를 저장하는 임시 데이터베이스 DB에만 저장하면 디스크 I/O 때문에 느려지기 때문에, 메모리 기반의 Redis에 로그인 정보를 저장하는 방식을 사용했습니다. 다만 Redis는 기본 설정에서 서버가 종료되면 데이터가 사라질 수 있다는 단점이 있습니다.
4단계: JWT (현재 많이 사용하는 방식)
JSON Web Token(JWT) **"로그인 정보를 꼭 서버나 DB에 저장해야 할까?"**라는 생각에서 나온 것이 JWT입니다.

JWT는 유저 정보를 담은 객체를 문자열로 만들어 서명한 뒤, 이 토큰(accessToken)을 브라우저에 전달합니다.
브라우저는 토큰을 저장해 두었다가 유저 정보가 필요한 API를 호출할 때 함께 보내고, 백엔드는 토큰을 검증해 사용자를 식별합니다.
JWT에는 ***해당 서버에서 정상적으로 발급되었음을 증명하는 서명(signature)***이 들어 있습니다.
그래서 DB를 조회하지 않고도 사용자를 식별할 수 있습니다.
암호화
로그인 정보를 조회했을 때 브라우저로 비밀번호가 전달되어서는 안 됩니다. 즉, 비밀번호는 알아낼 수 없어야 합니다. DB에 저장된 비밀번호를 알아낼 수 있다면, DB가 해킹당했을 때 유저의 비밀번호까지 노출되어 민감한 정보에 접근할 수 있게 됩니다.
암호화의 두 가지 방식
-
양방향 암호화 복호화가 가능한 암호화입니다. 즉, 암호화와 복호화를 모두 할 수 있습니다. (참고: JWT는 기본적으로 암호화가 아니라 서명 방식이라, 내용은 누구나 디코딩해서 볼 수 있습니다.)
-
단방향 암호화 (해시:
hash) 암호화는 되지만 복호화는 할 수 없는 방식입니다. 비밀번호처럼 민감한 정보는 해킹당해도 알아볼 수 없도록 단방향 암호화로 저장합니다.
해시가 중요한 이유
레인보우 테이블은 가능한 입력값과 해시값을 미리 계산해 둔 테이블입니다. 가능한 경우를 모두 대입해 보는 공격을 무차별 대입 공격(Brute Force Attack)이라고 합니다.
무차별 대입 공격을 막는 방법
해시 (키 스트레칭)
ex) 275719 —암호화—> 779
예를 들어 두 자리씩 끊어 10으로 나눈 나머지를 적으면 779가 됩니다.
나머지가 7이 되는 숫자는 27, 37, 47처럼 너무 많기 때문에 결과만 보고 원래 값을 알 수 없게 됩니다. 실제로는 여기에 솔트(salt)를 더하고 해시를 여러 번 반복해 공격을 더 어렵게 만듭니다.
댓글 0