티스토리 뷰

Web/정리글

JWT 사용이유

구름뭉치 2022. 5. 26. 16:39

Cookie, Session, JWT

이러한 방법들을 사용해서 사용자 정보를 갖는 이유는 HTTP의 Stateless한 특징 때문이다.

Cookie

HTTP의 무상태를 보완하기 위해 사용자 정보를 쿠키에 담아서 요청 시 함께 보내는 방법

매커니즘

  • 클라이언트(브라우저)가 로그인을 하면 서버가 응답 시 쿠키에 사용자 정보를 담아서 반환해준다.
  • 클라이언트는 사용자 정보를 저장하고 서버에 요청할 때마다 HTTP HEADER에 해당 쿠키를 실어서 보낸다.

주의사항

  • HTTPS가 아니라면 쿠키가 암호화되지 않으므로 노출될 가능성이 있다. 따라서 회원관련 정보는 담지 않는것이 좋다.
  • HTTP Only 설정 시 브라우저를 통한 요청이 아닌 Javascript 등을 통해 Cookie를 담아 요청하면 HTTP ONLY가 아니므로 쿠키를 전부 거절한다.
    -> HTTP Only : false 시 가능. 단, CSS(cross site scriping) 공격 등 보안에 취약해진다.

facebook의 경우set-cookie에 httponly가 되있다.


Session

쿠키의 단점을 보완하기 위한 방식으로 클라이언트가 사용자 정보를 보관하지 않고 서버가 보관하도록 한다. 이후 클라이언트는 SessionId를 가지고 서버에 요청하게 된다.

매커니즘

  • 클라이언트가 로그인하면 서버가 해당 회원 정보를 <SessionId - 회원 정보> 쌍으로 서버의 메모리에 저장한다.
  • 서버는 저장한 SessionId를 반환한다.
  • 클라이언트(브라우저)는 SessionId를 쿠키영역에 저장한다.
  • 서버에 요청을 보낼 때 마다 SessionId를 담아서 요청한다.

세션 통신 플로우

  1. 클라이언트 -> 서버로 최초 REQUEST를 보낸다.
  2. 서버는 세션 저장소에 세션ID와 작은 저장소 쌍을 같이 생성한다.
  3. 서버는 세션ID를 RESPONSE 메시지 Header에 넣고 반환해준다.
  4. 클라이언트의 웹 브라우저에 세션ID가 저장된다.
  5. 클라이언트가 서버에 Username&Password으로 로그인 요청을 보낸다. 이때 세션ID를 헤더에 담아서 보낸다.
  6. 서버는 DB로 가서 해당 회원이 존재하는지 확인한다.
  7. 해당 회원값이 유효하면 세션 저장소의 저장소에 회원객체를 저장한다.
  8. 로그인 성공 시 메인페이지.html 파일을 반환한다.
  9. 클라이언트가 사용자 정보 조회 요청을 보낸다. 이때도 세션ID를 가지고 온다.
  10. 서버는 세션 저장소에서 해당 세션ID가 존재하는지 확인하고 존재하면 -> 로그인이 된사람임을 알게된다.
  11. DB로 접근해서 사용자 정보를 조회한다.
  12. 사용자 정보를 클라이언트에게 반환해준다.

세션의 단점

  • 복수의 서버를 운영하는 경우 성능에 좋지 않다. 각 서버마다 Session 정보를 메모리에서 갖고 있어야 하므로 메모리 낭비가 발생한다.

예시

100명을 처리할 수 있는 서버가 있을 때 300명의 요청이 온다면 200명이 대기해야할 것이다. 이를 해결하기 위해 서버 2대를 증설해서 3대를 유지하는 경우 트래픽이 올 때 적절히 여유로운 서버로 요청을 보내는 로드밸런싱이 필요하다.

세션의 경우 서버의 인메모리에 존재하므로 여기서 문제가 발생한다.

  • 클라A -> 서버 A, B, C 중 B로 로그인을 했다.
  • B에 사람이 몰린 상황에서 A가 정보 조회를 요청했고, 로드 밸런싱이 C서버로 보냈다.
  • C서버는 A에 대한 정보가 없으므로 처음 온줄 알고 다시 로그인을 하도록 하게 된다.

이를 해결하기 위해 세션 저장소 간 정보를 동기화하거나 or 요청마다 처리 서버를 고정하는 방법이 있지만 둘다 좋지않다.
이를 위해 여러 서버가 공유하는 하나의 동일한 세션 저장소를 갖도록 하는 방법이 있다.

두가지가 있는데, 하드디스크를 사용하거나 or 리모트 인메모리 저장소(대표적으로 Redis)를 사용하는 것이다.

  • 하드디스크를 세션저장소로 사용하는 경우 가격은 저렴하지만 I/O 작업으로 인해 너무 느리므로 좋지않다.
  • 리모트 인메모리 저장소는 속도가 빠르므로 주로 사용하게 되는데, 물론 이 방법도 메모리는 가격이 비싸므로 만족스러운 해결법이 될 수 없겠다.
  • 클라이언트가 100, 1000이 아니라 100만, 1000만이라면 어떻게 할까?
    전체를 통합관리하는게 불가능할 것이고 일부만 관리한다면 캐시 HitRate가 줄어들고 오히려 쿼리는 캐시 + 물리저장소를 조회해야하므로 시간적으로 비효율적이게 될 것이다.

점점 더 트래픽이 올라가는 현 상황에서 세션 외 해결방법을 찾을 수 밖에 없고 세션 외 해결방법이 나왔다.

 

해결방법 1 (HTTP Basic)

  • HTTP Header에 Authorization 키의 값에 인증정보를 담는다. (ID, Password 등)
  • 이렇게 하면 요청 때마다 ID/PW를 갖고 요청하므로 세션, 쿠키를 만들 필요가 없어진다.
  • 단, HTTPS가 아닌 경우 HTTP 메시지가 암호화되지 않으므로 탈취 당하면 매우 위험하다.

해결방법 2 (HTTP Bearer)

  • HTTP Header에 Authorization 키의 값에 토큰을 담는다.
  • HTTPS가 아닌 경우 토큰방식도 노출되면 해당 토큰을 가지고 요청할 수 있으므로 위험하다.
    하지만, 토큰은 다시 로그인 할 때마다 새로 발급해주고 AccessToken의 유효시간을 짧게 가지므로 ID, PW가 노출되는 것보단 상대적으로 안전하다.
  • 이 토큰을 만드는 방법 중 하나가 JWT 방식이다.

보안 문제 CIA

JWT에 대해 알아보기전에 왜 JWT를 사용해야하는지 더 정확하게 이해해보자. 일단 세션 방식의 문제점을 알았고 추가적으로 보안성의 문제를 알아본다.

 

보안에 있어서 중요한 것은 CIA라고한다.

  • Confidentiality : 기밀성 (기밀문서의 기밀과 같다)
  • Integrity : 무결성 (Immutable 한 것이다)
  • Availability : 가용성 (신뢰하여 사용할 수 있는지)

이때, 기밀성과 무결성이 깨지면 자연스럽게 가용성이 없어지므로 둘을 지키는것이 핵심이다.

문제상황

1. 열쇠 전달의 문제 (기밀성)

A국가와 B국가가 있고, 중간에 문서를 전달하려고 한다. 이때 중간에 적국 C가 있어서 문서를 중간에서 열어보거나, 조작하거나, 탈취한다면 어떻게 CIA를 지키면서 안전하게 문서를 전달할 수 있을까?

 

문제) 문서를 열쇠로 잠그면 해당 문서를 열 수 없게 되므로 안전해진다. 그런데 B국가도 해당 문서를 열어보아야 하는데 열쇠가 없으니 열쇠를 받아야 한다. 그렇다면 그 열쇠는 어떻게 전달할 것인가?

 

2. 누가 보냈는가 (인증 문제, 무결성)

 

문제) 성공적으로 열쇠를 둘다 갖고 있다고 가정하자. 다시 A국가가 B에게 열쇠로 잠근 문서를 전달하려고 한다. 이때 똑같은 적국 C가 중간에 문서를 이제 열어볼 순 없다. 하지만 그 문서를 폐기시키고 다른 내용으로 문서를 보내거나 혹은 열쇠 전송 과정 중 C국가에게 열쇠 탈취 당해서 조작 후 보내진다면 어떻게 할 것인가? 즉, 누가 문서를 보낸 것인지 모르는 문제가 있다.

 

이것이 기밀성과 무결성이 지켜지지 않는 문제고 이로인해 가용성이 지켜지지 않게 된다. 이를 해결하기 위한 방법으로 RSA가 있다. 이에 대해 알아보자.

RSA

RSA는 위에서 말한 대칭키, 즉 잠그는 열쇠와 여는 열쇠가 동일한 방식이 아니라 비대칭키 방식으로 공개키/비밀키 방식의 암호화를 사용하는 방식이다. 이것을 통해 위 문제를 모두 해결할 수 있다.

 

1. 열쇠 전달의 문제 (기밀성)

 

해결) 공개키는 누구에게나 공개되어도 가능한 키이다. 이것은 자유롭게 전달해도 되는 키이므로 A, B, C는 모두 공개키를 갖고 있다. 이때 핵심은 A국가가 (B국가의 공개키)로 문서를 잠궈서 보내는 것이다. 이것을 열수 있는 키는 B국가의 비밀키 뿐이므로 확실히 암호화가 되었고 기밀성이 보장된다.

문제) C국가는 (B국가의 비밀키)가 없으므로 열어보고 조작할 수 없다.
다만 이 문서자체를 폐기해버리고 새로운 내용의 문서를 (B국가의 공개키)로 잠궈서 B에게 보낼 순 있다. 즉 누가 보냈는지 모르는 문제가 또 발생한다.

 

2. 누가 보냈는가 (인증 문제, 무결성)

 

해결) 이번에는 (A국가의 비밀키)로 문서를 잠궈서 보낸다. 이것은 (A국가의 공개키)로 열 수 있으므로 당연히 암호화의 역할을 없다. 하지만 A의 공개키로 열린다는 것은 A국가만이 소유하고 있는 비밀키로 잠궜음이 보장된다. 즉, 해당 문서를 (A국가의 공개키)로 열린다면 A국가가 보낸 것이 자명해진다.

 

3. 보안 문제 & 인증 문제

 

해결) 이제 두가지 문제를 모두 해결해보자. 핵심적인 부분이다. 먼저 (B국가의 공개키)로 문서를 잠근다. 해당 문서를 (A국가의 비밀키)로 다시 잠근다.

-> [A국가의 비밀키 ( B국가의 공개키 ( 문서 ) )] 와 같은 잠금이 이뤄지는 것이다.
이제 C국가는 A국가의 공개키로 열어도 B국가의 비밀키가 없으니 조작을 할 수 없으며, A국가의 비밀키가 없으니 C국가에서 새로운 문서를 보내도 B가 알 수 있게 된다. 이로서 열쇠전달의 문제와 누가 보냈는지에 대한 문제를 해결함으로서 기밀성과 무결성을 지킬 수 있게 되었다.

공개키로 잠근다는 것은 암호화를 의미하고, 비밀키로 잠근다는 것은 서명(인증)을 의미한다. 이를 적절히 혼합해 사용하여 CIA를 지킬 수 있게 된다. 이러한 보안 문제에 대한 이해를 바탕으로 JWT에 대해 알아보자.


JWT

HTTP Bearer 방식으로 <Authorization : token> 쌍으로 서버에 요청하게 된다.

구조

  • Header : 서명 알고리즘 정보가 있다.
  • Payload : Claim이 들어가며 일반적으로 사용자 관련 정보가 들어간다.
  • Signature : (Header + Payload + 비밀키)를 암호화한 값이 들어간다.

매커니즘 (HMAC 방식)

  • 클라이언트가 서버에 로그인을 요청한다.
  • 서버는 해당 로그인을 확인 후
    인코딩{ 알고리즘정보 } + .인코딩{ 회원 정보 } + .인코딩{ 암호화(알고리즘정보 + 회원정보 + 시크릿 값) }
    방법으로 Token을 생성한다. 이후 클라이언트에게 반환한다.
  • 클라이언트는 해당 토큰을 쿠키에 저장하고, 서버에 요청을 보낼 때 마다 HTTP HEADER에 포함해서 보낸다.
  • 서버는 해당 Token의 Signature값인코딩{ HMAC암호화(디코딩(토큰_알고리즘정보 + 토큰_회원 정보) + 시크릿 키 ) } 값이 같은지 비교해서 토큰의 유효성을 검증한다.

매커니즘 (RSA 방식)

  • 매우 유사하지만 Signautre를 생성할 때 비밀키를 포함하지 않는 방식이다.
  • (HEADER + PAYLOAD) 값을 (서버_비밀키)로 암호화해서 Signautre에 넣는다.
  • 클라이언트는 해당 토큰을 서버에 요청하고 서버는 (서버_공개키)로 복호화하므로서 해당 토큰이 유효한지 검증한다.

장점

  • 복수의 서버를 운영하더라도 각 서버들은  SecretKey만 가지고 전달받은 토큰의 유효성 검사만 하면 되므로 훨씬 부담이 적다.
  • 토큰 payload에 회원 데이터를 갖고 있으므로 세션서버에 조회를 하는 등의 시간이 발생하지 않는다.

주의사항

  • 토큰의 payload에 회원 데이터를 담긴하는데 이부분은 외부에 공개되는 부분이므로 구분값정도만 넣어야 한다. (ex. pk)

 

반응형

'Web > 정리글' 카테고리의 다른 글

SYN Flooding Attack(DDos) 대응 정리  (0) 2021.12.27
자바 웹 역사 정리  (0) 2021.12.21
멀티 쓰레드 정리  (0) 2021.12.20
HTTPS, TLS 정리  (0) 2021.10.19
DB 트랜잭션 정리  (0) 2021.10.04
Comments
반응형
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday