프리온보딩 챌린지

프리온보딩 FE JWT(2)

노엠디엔 2023. 10. 7. 01:27

원티드 프리온보딩 2일 차 강의는  JWT를 주제로 강의가 진행되었다.

 

일단 저번 시간에 로그인에 대해서 얘기를 해보았는데

https://zoon-bloom.tistory.com/133

 

프리온보딩 FE 로그인(1)

이번에 원티드 이벤트를 통해서 프리온보딩 FE 챌린지에 참여하게 되었는데 주제는 로그인 구현에 대한 내용이었다. 오늘은 첫 시간으로 로그인의 정의와 구조에 대해 설명을 들었는데 직접 mock

zoon-bloom.tistory.com

로그인에서는 주로 token을 사용하게 되는데 로그인 후에 사용자에게 token이란 것을 부여해서 마치 입장권을 주어

이후에 물건을 사거나, 저장하거나 하는 등의 행동들의 해당 사용자가 맞는지 확인하기 위해서이다.

토큰의 인증 절차는 다음과 같다고 함!

  • 요청: 사용자가 서버 또는 보호되는 리소스에 대한 액세스를 요청합니다. 
  • 확인: 서버가 해당 사용자의 액세스 여부를 확인합니다. 
  • 토큰: 서버가 링, 키, 휴대전화와 등의 인증 디바이스와 통신합니다.
  • 저장: 작업이 지속되는 동안 토큰이 사용자의 브라우저에 저장됩니다

https://www.okta.com/kr/identity-101/what-is-token-based-authentication

 

토큰 기반 인증이란? 인증 토큰의 종류와 JWT의 이점 | Okta Identity Korea

토큰 기반 인증이란 사용자가 자신의 아이덴티티를 확인하고 고유한 액세스 토큰을 받을 수 있는 프로토콜을 말합니다. 그렇다면 토큰 기반 인증에는 어떤 종류가 있는지, 그리고 이를 사용했

www.okta.com

 실제 서비스의 로그인 구조

강의에서는 실제 서비스라면 먼저 유저의 로그인을 시도하게 되고 클라이언트에 저장되어 있는
토큰이 있는지 확인하게 된다.

토큰이 있다면  토큰이 유효한지 확인게되는데.
유효하다면 서버와 연결 없이 바로 로그인하게 되고
유효하지 않다면 저장되어있던 토큰을 서버로 보내 토큰이 유효한지 검증하고 (validity check).

새로운 토큰을 보내면서 로그인하게 된다.

토큰이 없다면 id와 password를 서버로 보내 토큰을 만들고 클라이언트로 token을 보내게 된다.

 

그럼 토큰(jwt)은 어떻게 만들어질까?

JWT의 주요 특징:

  1. 자가 포함(Self-Contained): JWT는 필요한 모든 정보를 자체적으로 포함하고 있습니다. 이 정보에는 클레임(claims)이라고 불리는 데이터가 포함되며, 클레임은 사용자에 대한 정보, 권한, 기타 메타데이터 등을 포함할 수 있습니다.
  2. 디지털 서명(Digital Signature): JWT는 보안을 강화하기 위해 디지털 서명을 사용할 수 있습니다. 서버에서 생성된 JWT는 비밀 키를 사용하여 서명되며, 클라이언트는 이 서명을 검증하여 토큰의 무결성을 확인할 수 있습니다.
  3. 간결성 및 전송 가능성(Compact and Transportable): JWT는 간결하고 경량 한 형식을 가지고 있어 HTTP 헤더, URL 매개변수 또는 요청 본문에 쉽게 포함될 수 있습니다.
  4. 기본적으로 안전하지 않음(Inherently Unsafe): JWT는 클라이언트에서 생성 가능하므로 데이터 위조를 방지하기 위해 디지털 서명을 사용해야 합니다. 또한, 토큰의 유효 기간을 관리하여 액세스를 제한해야 합니다.

 

 마지막으로 jwt는 무상태성(Stateless)을 가진다! 

무상태성이란 서버가 클라이언트의 상태를 보존하지 않음을 의미한다.

서버는 클라이언트를 기억하지 않으며 오직 클라이언트의 요청에 대한 응답만 준다.

HTTP 통신은 기본적으로 무상태성(Stateless)을 지향한다.
왜냐하면 서버를 무한 확장 가능하기 때문이다.
=> 고객이나 요청(트래픽)이 갑자기 증가할 때 서버를 추가하기 쉬워진다.

만약 서버가 stateful하다면, 서버 하나가 장애가 났을 때
그 서버와 연결된 클라이언트가 기존 작업을 처음부터 다시 해야 하는 상황이 발생한다.
(예: 사이트 튕기면 결제를 처음부터 다시 해야 하는 것)

그러나 서버가 무상태성을 가진다면, 클라이언트 쪽에서 고객의 정보를 보관하므로
한 서버에서 장애가 나도 다른 서버에서 얼마든지 이후 요청을 처리할 수 있다.

무상태성의 단점은 클라이언트가 서버한테 요청할 때 추가로 전송해야 할 정보가 늘어난다.

이렇게 하면 요청/응답 시 오가는 payload가 커질 수 있기 때문이다?.

 

로그인 토큰에 많이 사용되고 있는 jwt 토큰 방식을 이용하기 때문에 jwt 초점에 맞춰 설명을 해주셨다.

jwt는 Header ,  Payload , Signature로 구성되어 있다.

 

Header 헤더는 토큰의 타입 및 사용하는 알고리즘을 나타내는 JSON 객체이다.

{
  "alg": "HS256",
  "typ": "JWT"
}

Payload 페이로드는 페이로드는 클레임(claims)이라고도 불리는 정보를 포함하는 JSON이다.

클레임에는 여러 가지 정보가 들어갈 수 있는데 두 가지 주요 유형이 있다.

  • Registered Claims: JWT 표준 스펙에 정의된 클레임으로, 사용자 정보, 만료 시간 등이 여기에 포함됩니다.
  • Custom Claims: 애플리케이션 고유의 클레임으로, 사용자 정의 데이터를 포함할 수 있습니다.

Signature (서명): 서명(Signature)은 토큰을 인코딩하거나 유효성 검증을 할 때 사용하는 고유한 암호화 코드이다. 
헤더(Header)와 페이로드(Payload)의 값을 각각 BASE64 Url로 인코딩하고, 인코딩한 값을 비밀 키를 이용해
헤더(Header)에서 정의한 알고리즘으로 해싱을 하고, 이 값을 다시 BASE64 Url로 인코딩하여 생성한다.

 

  1. JWT 생성: 서버는 JWT를 생성하고, 헤더와 페이로드를 조합한 후 서버의 비밀 키를 사용하여 서명을 생성합니다.
  2. JWT 전송: 클라이언트에게 생성된 JWT를 전송합니다. 이 JWT는 클라이언트가 인증되고 권한을 부여받을 수 있는
    티켓과 같은 역할을 합니다.
  3. JWT 검증: 클라이언트는 서버에 대한 요청을 할 때 JWT를 함께 전송합니다. 서버는 JWT를 검증하기 위해 클라이언트와 서버 간에 미리 공유한 비밀 키를 사용합니다.
  4. 서명 검증: 서버는 수신한 JWT의 헤더와 페이로드를 다시 조합한 후 클라이언트가 보낸 서명과 비교합니다. 이 비교를 통해 JWT가 변조되지 않았는지 확인할 수 있습니다.

토큰을 암호화하는 방식은?!(해싱)

토큰을 암호화하는 방식은 주로 두 가지 접근 방법이 있다고 한다
대칭 키(암호화 및 복호화에 동일한 키 사용)와 비대칭 키(암호화와 복호화에 각각 다른 키 사용)

  1. 대칭 키 암호화:
    • 동일한 키 사용: 대칭 키 암호화에서는 동일한 키를 암호화와 복호화에 모두 사용합니다.
      이러한 키를 공유키(shared key) 또는 비밀 키(secret key)라고 합니다.
    • 단순하고 빠름: 대칭 키 암호화는 암호화 및 복호화가 간단하며 빠르게 수행됩니다.
    • 키 공유의 어려움: 주요 고민 사항은 키의 안전한 공유입니다. 키가 유출되면 데이터의 기밀성이 위협받을 수 있습니다.
    • 예시: AES (Advanced Encryption Standard)는 대칭 키 암호화의 한 예입니다.
  2. 비대칭 키 암호화:
    • 공개 키 및 비밀 키: 비대칭 키 암호화에서는 암호화와 복호화에 서로 다른 키 쌍을 사용합니다. 이 중 하나는 공개 키(public key)로 알려져 있고 다른 하나는 비밀 키(private key)로 유지됩니다.
    • 안전한 데이터 교환: 공개 키는 공개되어 있어 누구나 사용할 수 있지만, 비밀 키는 오직 소유자만 알고 있습니다. 이를 통해 안전한 데이터 교환 및 디지털 서명이 가능해집니다.
    • 암호화 및 복호화가 느림: 비대칭 키 암호화는 대칭 키 암호화보다 느릴 수 있습니다.
    • 예시: RSA, ECC (Elliptic Curve Cryptography)는 비대칭 키 암호화의 예입니다.

jwt에서는 비대칭 키 암호화를 주로 사용한다고 하는데 암호화된 키는 해싱방식으로 암호화되어 단방향의 특징을 가지는데 유저의 아이디와 비밀번호로 해시된 암호는 암호화 키를 찾을 수 있지만 암호화된 키로는 찾을 수 없는 특징을 가진다.

 

JWT의 유효성 검증 방식

클라이언트는 JWT의 헤더와 페이로드를 서버로 전달하고, 서버는 서명을 사용하여 해당 토큰의 무결성을 검증하게 되는데 이때 서버는 서버 측에 미리 저장된 비밀 키(secret key)를 사용하여 signature서명을 검증합니다.

따라서 클라이언트는 JWT의 서명을 생성하지 않고 서버에서 JWT를 생성하고 서명을 포함시킨 다음 클라이언트로 전달하고, 클라이언트는 해당 토큰을 서버로 다시 전송하여 서버에서 유효성을 검증합니다.

이 부분은 아직 정확히 이해한 건지는 모르겠지만 서버에서는 암호화 키를 설정하고 이때 이 암호화키는 jwt에 signature 서명에 사용되어 jwt 토큰을 암호화하여 클라이언트로 보내지는 것이기 때문에 절대적으로 유출되며 안되며

클라이언트에서는 jwt 토큰을 보내면 서버에 설정되어 있는  암호화 키로 클라이언트에서 보낸
jwt 토큰을 유효성을 검증하는 것 같다.

 

JWT 보관 방식과 보안

jwt 토큰을 보관 방식과 어떻게 이용하는지에 따른 보안 사고가 있다고 한다.

1. 시크릿 키 노출 : 서버에서 노출되어 말 그래도 대형 사고 정말 실제 서비스에서 일어난다고 하면 

모든 유저 정보들이 다 탈취당할 가능성이 있으므로 DB 자체를 싹 다 리셋할 수도 있다고 함?!

2. 데이터 복호화로 인한 정보 노출 : JWT의 내용은 Base64로 인코딩 되어 있기 때문에 누구나 디코딩하여 내용을 확인할 수 있다고 함!

3. 토큰 탈취 :  localstorage 나 어떠한 방식 (하드를 훔치거나 물리적인?)을 통해 토큰을 탈취당할 경우

 

강의에서는 jwt 탈취 방법 대한 두 가지 해킹 방법에 대해서 설명해 주었는데

로컬 스토리지/쿠키 저장 시 생기는 해킹 - XSS(Cross Site Scripting)

XSS (Cross-Site Scripting)는  해커가 악성 스크립트를 웹 페이지에 삽입하여 다른 사용자의 웹 브라우저에서 실행되도록 하는 공격 아다. 해커는 다른 사용자의 세션 정보를 훔치거나 웹 페이지의 내용을 조작할 수 있는 방법

React에서는 CSR 클라이언트 사이드 렌더링을 사용하기 때문에 index.html 파일에서 스크립트를 추가하여 

해당 페이지의 localstorage의 접근해  토큰을 탈취할 수 있다고 한다!

 

쿠키 저장 시 생기는 해킹- CSRF(Cross-site Requeset Forgery)

CSRF(Cross-site Requeset Forgery)는 해커가 스팸 메일이나 의도적으로 사용자를 해킹 웹사이트로 유도해서 

해당 사이트의 악의적인 요청을 서버로 전송하는 스크립트가 실행되고. 해커는 사용자의 브라우저에
JWT 토큰을 요청하는 request 보내도록 스크립트를 작성해서 보내게 된다.

 

이러한 보안 강화를 위해 강의에서는 accessToken과 refreshToken토큰을 사용하여 

(리프레쉬 토큰을 accessToken을 발급만 해주는 역할 한다)
refreshToken을 스토리지에 저장한다면 리프레시 토큰만 로컬 스토리지에 저장하고

액세스 토큰은 메모리에서만 사용하는 방식을 많이 쓴다고 한다.

리프레시 토큰이 탈취되어도 액세스 토큰이 메모리에서만 사용되어
짧은 만료 시간을 주어 보안을 강화하는 방식인 것 같다.

 

또 다른 방법으로는  리프레시 토큰을 HttpOnly 쿠키에 저장하고, Secure 속성을 사용하여 HTTPS 연결에서만 전송되도록 설정하면. 리프레시 토큰이 클라이언트 측 JavaScript로부터 접근 불가능하며, CSRF 공격에서도 보호된다고 한다!.

 

이번 강의는 들으면 들을수록 이해하기 어려운 부분들이 많았던 것 같다. 특히나 보안 쪽은 정말 상상할 수 없는공부한 내용 외에도 해킹 공격을 가할 수 있기 때문에 보안팀이 존재하는 것 같다. 프런트에서는 token 어떠한 방식으로 저장할지에 대해서 중요하게 들었고 듣다 보면 프런트에서는 받아서 쓰는 것 외에는 token을 어떻게 할 수는 없을 것 같다. 그렇기 때문에 서버 쪽에 secret key는 프런트에서도 모르는 게 좋으며 어떠한 payload 나 민감한 정보에 프런트가 접근하는 것은 좋지 않은 방식이라는 추가 조언도 있었다.

 

참고블로그 : https://velog.io/@yena1025/JWT-%ED%86%A0%ED%81%B0%EA%B3%BC-%EB%AC%B4%EC%83%81%ED%83%9C%EC%84%B1Stateless