SIU
article thumbnail

Client - Server 구조

1번) 전통적인 Client - Server 구조는 Cline(Front)가 있고 통신할 API Server가 있고 아이디와 비밀 번호를 검증할 수 있는 Database가 있습니다. 만약, 1번 아키텍처를 사용하는 서비스의 이용자가 많아졌을 때 어떤 현상이 발생할까? API Server는 스케일 업(Scale-up)과 스케일 아웃(Scale-out)으로 서버 증강을 진행한다.(서버를 늘린다) 문제는 Database에서 발생한다. 병렬처리(분산처리)가 쉽지 않고 서버보다 증강 비용이 많이 든다. 또한, Clinet의 데이터를 보관해서 따르는 보안, 관리 부담감이 생긴다. 

 

2번) Client - Server 구조는 JWT(JSON Web Token) 를 활용하여, 가장 관리하기 번거로운 Database를 없앤다. 유저데이터를 유저가 직접 관리하게 할 수 있다. 유저가 유저데이터를 가지고 있다면, 자신의 데이터를 수정할 수 있다고 생각할 수 있다. 하지만, JWT를 이용하면, 유저가 자신의 데이터를 볼 수는 있는데 수정을 못 한다. 그리고 만약 데이터를 수정하고 싶으면, 서버를 통해서만 수정이 가능하다.

 

 

JWT 디코더
JSON 데이터 분석

JWT는 헤더(Header), 페이로드(Payload), 서명(Signature)으로 구성되어 있다. 헤더는 어떤 데이터를 다룰지를 정하는 데이터 타입, 암호화(해싱 알고리즘) 정보를 담습니다. 페이로드는 Json 형태의 실제 보관할 데이터를 저장합니다. 헤더와 페이로드는 암호화가 되어 있지 않고, 데이터를 주고 받기 편하게 base64 포맷으로 변환만 되어있습니다. 디코더 사이트에서 토큰 정보를 넣으면 해독된 헤더와 페이로드 정보를 얻을 수 있습니다. JWT에서 가장 중요한 부분은 서명(Signature : 시그니쳐)이다. 시그니쳐 덕분에 유저는 데이터를 보기만 할 수 있고(Read-Only) 서버에서만 (can Only Modify Data) 수정이 가능하다. 시그니쳐는 client에서는 수정할 것이 없고 그저 주고받는 데이터이다. 

 

 

시그니처의 구조

시그니처 관리는 서버에서만 한다.  클라이언트에서 서버에 JWT 토큰을 보내면, 서버는 JWT가 임의로 조작이 되었는지 확인합니다. JWT의 정보가 수정되었는지 확인하는 용도로 시그니처가 사용된다. 헤더와 페이로드를 조합하고 비밀키를 이용해서, 해쉬값을 만들어 내는데 이것이 해쉬값이 된다. 서버에서 바로 할 일은 Client(유저)가 보내준 JWT의 헤더와 페이로드를 이용해서, 서버만 가지고 있는 비밀키를 조합해서(해쉬 값) 직접 시그니처를 만들어 보는 것이다. 그러고나서, 유저가 기존에 보내주었던 시그니처 값과 비교를 한다. 만약, 값이 다르면 무언가 조작된 데이터라는 것을 의미한다. 그래서, 비밀키(Secret Key)는 서버에서만 알고 있어야 하고, 클라이언트는 절대 알면 안되는 값입니다.

 

JWT 덕분에,

1. 데이터 보관은 유저가 할 수 있다

2.  유저가 자신의 데이터를 보는 것은 가능한데, 수정은 유저가 할 수 없다.

3. 데이터를 수정하려면, 서버에 JWT를 보내서 서버에서만 수정이 가능하다.

 

JWT 보안성, 과연 안전할까?

 

IF. 토큰이 탈취(유출) 된다면,

서버는 토큰을 전체 초기화 하는 방법 외에 해당 토큰을 막을 방법이 없다. 강제로 탈취된 토큰만 만료시킬 방법이 없다.

 

1) 토큰 유효 기간을 짧게 가져간다.

- 사용자 매번 로그인 불편함 경험 

2) JWT 블랙리스트를 세션 스토리지로 만들어서, 토큰 검사

- JWT의 장점을 살리지 못한다. (session 방식이랑 별차이 없음, Stateless 하지 않다)

3) JWT로 내부 시스템에서 인증 처리를 전체로 하기 보다는, session 관리는 원래 하던 방식으로 가져가고 JWT를 복사하고 휴대하기 쉬운 cookie 정도로 사용하면 좋지 않을까.

 

Access Token, Refesh Token 2개로 운용

토큰 유효기간을 짧게하면, 사용자는 로그인을 자주 해서 새롭게 토큰을 발급받아야 하므로 불편하다는 단점이 있다. 그렇다고 유효기간을 늘리면, 토큰을 탈취당했을 때 보안이 더 취약해지게 된다.

"유효기간을 짧게 하면서 사용자의 로그인 불편함을 개선하는 해결책이 뭐가 있을까" 바로, Refresh Token이다. Access Token은 접근(인증)에 관여하는 토큰이고, Refresh Token은 재발급에 관여하는 역할을 수행한다.

 

Access Token은 인증을 하는 용도로 사용하고, Refesh Token은 Access Token이 만료 되었을 때, 새로 발급을 하는 용도로만 사용한다.

 

예를 들어, 처음 로그인에 성공했을 때, 서버는 클라이언트에게 Access Token과 Refresh Token을 동시에 발급한다. 서버는 데이터베이스에 Refresh Token을 저장하고, 클라이언트는 Access Token과 Refresh Token을 쿠키, 세션 혹은 웹 스토리지에 저장하고 요청이 있을 때마다, 이 둘을 헤더에 담아서 보낸다.

이 Refesh Token은 긴 유효기간을 가지면서, Access Token이 만료됐을 때 새로 재발급해주는 열쇠가 된다. 따라서 만료된 Access Token을 서버에 보내면, 서버는 같이 보내진 Refresh Token을 DB에 있는 것과 비교해서 일치하면 다시 Access Token을 재발급하는 간단한 원리이다. 그리고 사용자가 로그아웃을 하면 저장소에서 Refresh Token을 삭제하여 사용이 불가능하도록 하고 새로 로그인하면 서버에서 다시 재발급해서 DB에 저장한다.


RTR (Refresh Token Rotation)


RTR로 refresh token을 한번만 이용하게 할 수 있다. refresh token이 사용 될 때마다 새로운 access token과 refresh token을 발급하여 이전에 발급된 token들은 사용이 불가하다.

refresh token이 1회 이상 사용된다면 무언가 잘못됐다는것을 감지하여, 즉시 기존의 refresh token을 무효화시킨다. 이로 인해 하위의 refresh token들도 무효화되며 token체인이 더이상 유효하지 않게 된다. 즉, RTR을 사용하면 도난당한 refresh token의 사용을 감지할 수 있다.

 

Redis 도입

Redis의 장점은 I/O가 빈번한 데이터를 저장할 때 사용하기 좋다는 점과 운영 중인 웹 서버에서 key-value 형태의 데이터 타입을 처리해야 할 때 사용하기 좋다는 장점이 있다. 또한, Redis의 장점으로는 사용자 세션 관리인데 사용자의 세션을 유지하고 불러오고 여러 활동을 추적할 때 효과적으로 사용할 수 있다.

 

JWT 특성상 한번 발급된 토큰은 삭제/수정이 불가능하기 때문에 인메모리 데이터를 이용해 빠르고 효율적으로 로그아웃 처리를 할 수 있다. 

 

 

 

대칭키 암호화 방식(symmetric-key algorithm) 사용 

* 암호화와 복호화에 같은 암호 키를 쓰는 알고리즘

 

일반적으로, JWT 라이브러리에서 사용하는 암호화 라이브러리 기본 값은 "HS256(HMAC with SHA-256)" 입니다. 즉, 대칭키 형식의 암호화 알고리즘으로서, 시크릿 키카 암호화, 복호화에 모두 사용됩니다.

따라서, 시크릿 키가 만약에 탈취된다면 탈취자가 자유롭게 토큰을 변조가 가능합니다. 상대적으로 비대칭키(공개키) 방식보다 안전하지 않을 수 있습니다.

따라서, "RS256"과 같은 비대칭키 암호화 암고리즘을 사용하는 방법이 있습니다. 공개키는 탈취되어도 Private key를 모르면 공격자는 토큰의 변조가 불가능합니다. 

 

 

 

'CS' 카테고리의 다른 글

외부 조회 API 연동 장애 대응책  (0) 2024.02.04
[네트워크 보안] HTTPS 복호화와 암호화  (0) 2023.04.04
profile

SIU

@웹 개발자 SIU

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!