배경
애니메이션 추천 서비스를 개발하면서 JWT를 사용했다. 사용자가 로그인할 때 인증할 필요가 있었고 여러 방법을 비교해본 결과 토큰을 사용한 방식이 그나마 가장? 안전했고 대표적인 토큰 방식인 JWT를 사용했다.
JWT토큰 이전에는 어떤 방식으로 인증했는지 그리고 왜 JWT가 탄생했는지에 대해 간단히 알아보자
인증과 인가
들어가기에 앞서 인증과 인가에 대한 개념을 잡고 가야 한다.
인증이란 식별 가능한 정보로 서비스에 등록된 유저의 신원을 입증하는 과정이다.
풀어서 설명하면 회사의 사원증과 같은 개념으로 닉네임이나 사진 같은 정보로 어떤 회사의 회원인지 확인하는 절차이다.
인가란 인증된 사용자에 대한 자원 접근 권한을 확인하는 것이다.
사원증을 예로 들면 인턴의 경우 회사 내에 접근 불가능한 곳이 몇몇 곳 존재한다. 인가란 해당 사용자의 권한으로 접근이 가능한지 불가능한지 확인하는 것이다. 게시판의 경우 다른 사람의 게시글은 삭제 불가능하다던지 등등...
JWT는 인증할 때 사용하는 방식이다. 그렇다면 인증에 관해서 어떤 방법들이 있을까?
- 인증하기: Request Header
- 인증 유지하기: Browser
- 안전하게 인증하기: Server
- 효율적으로 인증하기: Token
1. 인증하기
접속하고 싶은 사이트의 url에 아이디와 비밀번호를 담아서 요청을 보내면 인증이 된다. 브라우저는 아이디와 비밀번호를 인코딩(Base64)해서 요청 헤더(Authorization)에 담아서 서버로 보낸다.
단점
위 방식의 경우 인증이 필요할 때마다 매번 아이디와 비밀번호를 보내야 한다.
2. 인증 유지하기
사용자가 매번 아이디 비밀번호를 입력하지 않고 한번 인증했으면 서버에서 리턴한 id와 비밀번호를 쿠키에 담아놓고 인증이 필요한 요청의 경우 쿠키에 있는 값을 다시 암호화해서 헤더에 담아 보내면 된다.
단점
위 방식은 해커에게 취약하다. 클라이언트는 서버보다 상대적으로 보안에 취약하기 때문에 쿠키나 스토리지에 담긴 정보만 가져오면 마음대로 인증을 할 수 있다.
3. 안전하게 인증하기
첫 요청은 똑같다. 사용자가 아이디와 비밀번호를 담아서 서버로 보내면 서버는 아이디와 비밀번호를 DB에 저장한다. 서버는 키와 밸류로 구성된 세션을 생성해서 아이디를 랜덤 한 값으로 변경한 뒤 클라이언트로 보낸다. 클라이언트는 랜덤한 값을 저장한다.
세션으로 작성해서 보내면 바로 알아볼 수 없고 만료 기간을 설정할 수 있기 때문에 편리하다. 또한 탈취됐을 때 서버에서 만료 처리하면 된다.
응답할 때 JESSIONID에 담아서 보낸다.
단점
위 방식은 서버가 여러 대 있을 경우 문제가 발생한다.
1대의 서버에서 세션 키와 벨류 값을 저장해서 가지고 있으면 클라이언트는 그쪽 서버로밖에 요청을 못 보낸다. 클라이언트는 세션으로 담아서 요청을 보내야 하기 때문에 각각의 서버에서 세션을 관리하고 있으니 다른 서버에서 해당 세션을 읽을 수 없다.
세션을 db에 저장하면 되지 않을까?라고 생각하시는 분들도 있을 거다.
그래서 DB에 저장하는 방식으로 위 문제를 해결했다.
하지만 DB에 저장하는 방식도 문제가 있다. 클라이언트가 많아질 경우 DB에 세션 값이 너무 많이 쌓여서 DB에 장애가 발생할 수 있다.
여기서 드는 의문은 이런 문제는 도대체 왜 발생할까??
그건 HTTP의 특징 때문이다. HTTP는 상태를 유지하지 않는다.
하지만 인증과 인가는 계속적으로 상태를 확인해야 한다. 상태를 가지지 않는 HTTP와 상태를 가져야 하는 로그인 요청은(클라이언트, 서버) 충돌이 발생한다.
클라이언트에도 상태를 맡기고 서버에도 맡겼지만 문제가 발생했다.
마지막 남은 방법! 요청과 응답에 사용자의 상태를 담아보자! 이것이 토큰이다.
대표적인 토큰 JWT
대표적으로 JsonWebToken이 있다.
시크릿키를 사용해서 토큰을 만들어낸다. 시크릿키를 사용해서 토큰에 인증 과정을 거친다.
JWT자체는 해독하기 쉬워서 JWT에는 민감정보(비밀번호)를 담지 않는다.
JWT를 잘 관리하려면 시크릿키를 서버 내부에서 잘 관리해야 한다.
4. 효과적으로 인증하기
첫 요청은 똑같이 보낸다. 이후 시크릿키를 사용해서 JWT를 생성하고 응답을 보낸다. 이후 클라이언트는 쿠키에 토큰 값을 저장한다. 클라이언트가 다음번 요청을 보낼 때 JWT토큰을 보내고 서버는 본인이 가진 시크릿키를 사용해서 토큰이 유효한지 파악하고 유효하다면 사용자 정보를 파악한다.
토큰에 이름, 만료시기, 권한 등을 담아서 토큰을 해독한다.(비밀번호 사용 불가) 서버가 여러대로 늘어나도 시크릿키만 가지고 있으면 된다.
하지만 위 방식은 액세스 토큰이 탈취당하면 사용자와 똑같은 직위를 가질 수 있기 때문에 문제가 있다.
그래서 액세스 토큰에 만료기한을 짧게 만들고 서버에 리프레쉬 토큰을 생성해서 액세스 토큰을 생성한다.
사용자가 첫 요청을 보낼 때 리프레쉬 토큰은 DB에 저장한다. 이 둘을 한 번에 클라이언트에 응답하고 클라이언트는 둘을 저장한 후 액세스 토큰을 통해서 요청을 주고받는다.
액세스 토큰이 만료됐다면 서버에서 만료됐다고 알려주고 브라우저는 액세스 토큰과 리프레쉬 토큰을 같이 담아서 요청을 보낸다. 이후 서버는 리프레쉬 토큰 값을 확인하고 액세스 토큰을 새로 생성해서 응답을 보낸다.
주의사항
위 방식처럼 토큰을 인증하더라도 토큰 자체를 탈취당하면 위험하다. 보안은 항상 신경 써야 한다.
리프레쉬 토큰을 사용하더라도 탈취당하면 똑같다. 하지만 액세스 토큰만 사용할 때는 매 요청마다 토큰을 담아서 보내서 어떤 요청에서든 탈취당하면 끝이지만,
리프레쉬 토큰은 액세스 토큰이 만료됐을 때만 리프레쉬 토큰을 담아서 보내니 이 타이밍에만 탈취당하지 않으면 안전하다.
'IT > 웹개발' 카테고리의 다른 글
도커와 도커컴포즈 (0) | 2022.10.30 |
---|---|
CI CD with 젠킨스 (0) | 2022.10.30 |