[Javascript] JWT

[Javascript] JWT

JSON Web Token session? token?

JWT가 뭔가요?

회원 인증 시스템 등 유저를 인증하고 식별하기 위해 필요한 기술로 JSON으로 이루어진 토큰을 말한다. 먼저 유저 식별에 사용할 수 있는 방식에는 두 가지가 있다.

세션 기반 인증 시스템

간단하게 서버가 사용자임을 기억하는 것이다.
사용자가 로그인하면 세션 id를 기준으로 세션을 서버에 저장한다.
브라우저에서는 세션 id가 쿠키로 저장된다. 그 다음 클라이언트가 요청을 보낼 때마다 쿠키에 세션 id를 함께 전송한다.
서버는 클라이언트가 보낸 세션 id를 세션 저장소(메모리, 디스크, 데이터베이스 등)에서 세션을 조회하고 응답한다.

  • 장점: 서버 측에서 관리하기 때문에 클라이언트 영향을 받지 않아 데이터 손상 우려가 없고 상대적으로 안전하다.
  • 단점: 서버 인스턴스가 여러개가 되면 이 서버들이 같은 세션을 공유해야 하므로 전용 데이터베이스가 필요하다. (확장이 번거롭다.)
    세선 정보를 서버에 들고 있다는 부담이 있다.

토큰 기반 인증 시스템

토큰은 로그인 후 서버가 만들어주는 문자열이다. 토큰에는 사용자 로그인 정보, 이 정보가 서버에서 발급되었음을 증명하는 서명(해싱 알고리즘으로 만들어짐)이 들어있다.
사용자가 로그인하면 서버에서 토큰을 발급해주고 클라이언트는 발급된 토큰을 (일반적으로 로컬 스토리지에) 저장한다.
클라이언트가 요청을 보낼 때 발급받은 토큰을 헤더에 포함시켜 보낸다.
서버는 토큰이 유효한지 확인하고, 응답한다.

  • 장점: 서버에서 사용하는 리소스가 적어 메모리 부담이 비교적 적고, 서버의 확장성이 높다.
  • 단점: 비교적 손상 위험이 있다. XSS(크로스 사이트 스크립팅) 공격에 취약할 수 있어 민감한 정보 포함은 피해야 한다.

JWT의 구조

. 으로 구분되는 header, payload, signature로 구성된다.

토큰의 타입과 해싱 알고리즘의 정보를 가지고 있다.

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

Payload

토큰에 담을 정보를 가지고 있다.

{
  "sub": "1234567890",
  "iat": 1516239022,
  "https://sssseulg2.github.io/": true,
  "admin": true
}

key-value 형식의 정보를 클레임(Claim)이라 부른다. 클레임도 세 가지로 나눈다.

  • 등록된 클레임 (registered claim)
    이미 정해진 종류의 데이터들로, 사용이 optional하다. iss, sub, aud, exp, nbf, iat, jti가 있다.
  • 공개 클레임 (public claim)
    사용자 정의 클레임이며, 공개용 정보를 위해 사용된다. 충돌이 방지된 이름을 가지고 있어야 해서 클레임 이름을 URI 형식으로 짓는다.
    {
    "https://sssseulg2.github.io/": true
    }
    
  • 비공개 클레임 (private claim)
    사용자 정의 클레임이며, 양 측(주로 클라이언트와 서버)간에 임의로 지정한 클레임 이름들이다. 이름 중복으로 충돌이 될 수 있어 유의해야 한다.
    {
    "username": "seulgi"
    }
    

    Signature

    헤더와 페이로드를 Base64로 인코딩한 후 해시함수를 적용하고 개인키로 서명한 전자서명이 담겨있다.
    전자서명은 헤더와 페이로드가 변조되었는지 확인하기 위해 사용되며, JWT를 신뢰하는 근거가 된다.
    전자서명은 비대칭 암호화 알고리즘을 사용한다. 암호화에는 개인키를 복호화에는 공개키를 사용한다.

    //서명 부분의 수도코드는 다음과 같다.
    HMACSHA256(
    base64UrlEncode(header) + "." +
    base64UrlEncode(payload),
    secret)
    

마치며

JWT는 다른 토큰과 달리 토큰 자체가 데이터를 가지고 있다. 일반적인 토큰은 데이터베이스에 토큰을 저장해놓고 요청이 있을 때 유효한 토큰인지 등 검사해 유효한 경우 사용자로 인식하고 정보를 조회하는, 요청마다 데이터베이스를 조회해 비용이 크다.
JWT는 토큰이 유효하면 클레임셋을 디코딩해 데이터를 열어보고, 토큰이 사용 가능한지 검사하고 정보를 조회할 수 있다. 클레임 셋에 사용자 정보가 들어 있으면 데이터베이스, 캐시를 조회할 필요가 없다. 하지만, 클레임 셋의 내용이 많아질수록 토큰의 길이가 길어진다. 그래서 너무 많은 정보를 클레임셋에 담지 않는 것이 좋을 것 같다.(클레임 이름도 짧은 것이 좋음!)
또한, 클레임셋은 암호화되지 않기 때문에 보안이 중요한 데이터는 넣지 않는 것이 좋겠다.

클레임 셋에는 필요한 최소한의 정보만을 넣도록 하자!