[1/2 JWT로 보안수준 높이기] JWT를 Stateful하게?

BlackBean99·2022년 9월 7일
3

Server

목록 보기
1/2

JWT를 JWT스럽지 못하게 설계를?
왜?
뭘 위해서 그렇게 쓰는거야?

JWT를 Stateful하게 설계하는 이유를 설명하기위한 첫번째 포스트를 쓰기 전에
JWT에 대한 소개글을 먼저 작성하겠습니다.

JWT(JSON Web Token)란

세션과 달리 토큰을 Client에 저장해서 서버의 부담을 줄일 수 있다. 토큰 자체에 사용자의 권한 정보나 서비스를 사용하기 포함된다는 것.

Stateless인 환경에서 사용자 데이터를 주고 받을 수 있다.

단순히 JWT에 토큰을 클라이언트에 저장하고 요청할 때 HTTP 헤더에 토큰을 첨부하는 것만으로 데이터를 요청하고 응답을 받을 수 있다는 장점!


JWT의 구조

Header, Payload, Signature로 구성되고 각 구분은 . 으로 합니다.

Header(헤더)

JWT에 사용할 타입(JWT 라는 문자열이 들어감) 과 해시 알고리즘의 종류가 담긴다.

Payload(정보)

서버에서 첨부한 사용자 권한 정보와 데이터가 담겨있다.

정보를 담는 단위를 클레임(Claim)이라고 부릅니다.

  • ㅁ토큰에 대한 정보를 담기 위한 클레임들이며, 이미 이름이 등록되어있는 클레임

  • iss : 토큰 발급자(issuer)

  • sub : 토큰 제목(subject)

  • aud : 토큰 대상자(audience)

  • exp : 토큰의 만료시간(expiraton). 시간은 NumericDate 형식으로 되어있어야 하며,(예: 1480849147370) 항상 현재 시간보다 이후로 설정되어있어야한다.

  • nbf : Not Before 를 의미하며, 토큰의 활성 날짜와 비슷한 개념. NumericDate 형식으로 날짜를 지정하며, 이 날짜가 지나기 전까지는 토큰이 처리되지 않는다.

  • iat : 토큰이 발급된 시간 (issued at)

  • jti : JWT의 고유 식별자로서, 주로 일회용 토큰에 사용한다

Signature

Header, Payload를 Base64 URL-safe Encode하고 Header에 적힌 해시함수를 적용하고, 개인키(Private Key)로 서명한 전자서명이 담겨있다.

타원곡선 암호화를 한다고 가정하면 Signature값은

Sig = ECDSA(SHA256(B64(Header).B64(Payload)),PrivateKey)

발급 후의 토크의 정보는 수정할 수가 없습니다.


동작과정

  1. 클라이언트가 사용자의 아이디, 패스워드를 통해 웹 서비스 인증 요청
  2. 서버에 Signed 된 JWT를 생성해서 클라이언트에 응답으로 돌려주기
  3. 클라이언트 가 서버에 데이터를 요청할 때마다 JWT를 Http Header에 첨부한다.
  4. 서버에서 JWT를 검증한다.

이때 당시 JSON 데이터를 Base 64 URL-safe Encode를 통해 인코딩을 한다.

Base 64 URL-safe Encode는 일반적인 URL에서 오류없이 사용할 수 있도록 + , /- _로 나타낸다.

Token방식의 이점

  1. 사용자가 아이디와 비밀번호로 로그인을 한다.
  2. 서버 측에서 사용자(클라이언트)에게 유일한 토큰을 발급한다.
  3. 클라이언트는 서버 측에서 전달받은 토큰을 쿠키나 스토리지에 저장해 두고, 서버에 요청을 할 때마다 해당 토큰을 서HTTP 요청 헤더에 포함시켜 전달한다.
  4. 서버는 전달받은 토큰을 검증하고 요청에 응답한다.토큰에는 요청한 사람의 정보가 담겨있기에 서버는 DB를 조회하지 않고 누가 요청하는지 알 수 있다.

Token방식의 단점

  1. 쿠키/세션과 다르게 토큰 자체의 데이터 길이가 길어, 인증 요청이 많아질수록 네트워크 부하가 심해질수 있다.
  2. Payload 자체는 암호화되지 않기 때문에 유저의 중요한 정보는 담을 수 없다.
  3. 토큰을 탈취당하면 대처하기 어렵다. (따라서 사용 기간 제한을 설정하는 식으로 극복한다)

이런 Token 의 방식중 하나인 인증에 필요한 정보를 암호화 시킨 JSON 토큰을 JWT라 부른다.

실제로 토큰을 생성하는 과정을 통해 더 이해해봅시다.

생성 (in Java)

public String crateJwtToken() {
    Date now = new Date();

    return Jwts.builder()
        .setHeaderParam(Header.TYPE, Header.JWT_TYPE) // (1)
        .setIssuer("fresh") // (2)
        .setIssuedAt(now) // (3)
        .setExpiration(new Date(now.getTime() + Duration.ofMinutes(30).toMillis())) // (4)
        .claim("id", "아이디") // (5)
        .signWith(SignatureAlgorithm.HS256, "secret") // (6)
        .compact();
  }
  1. 헤더의 타입(typ)을 지정할 수 있습니다. jwt를 사용하기 때문에 Header.JWT_TYPE로 사용해줍니다.
  2. 등록된 클레임 중, 토큰 발급자(iss)를 설정할 수 있습니다.
  3. 등록된 클레임 중, 발급 시간(iat)를 설정할 수 있습니다. Date 타입만 추가가 가능합니다.
  4. 등록된 클레임 중, 만료 시간(exp)을 설정할 수 있습니다. 마찬가지로 Date 타입만 추가가 가능합니다.
  5. 비공개 클레임을 설정할 수 있습니다. (key-value)
  6. 해싱 알고리즘과 시크릿 키를 설정할 수 있습니다.

지금까지 JWT의 동작 과정 및 개념을 이해해보았습니다.
다음 포스팅에서 Stateless 한 JWT 를 강제로 Stateful하게 설계를 해서 XSS Attack을 막는 방법을 소개해보겠습니다.

Reference

profile
like_learning

0개의 댓글