JWT(JSON Web Token)
JWT (JSON Web Token)는 RFC 7519 문서에 정의되어 있으며 당사자 간에 정보를 JSON 개체로 안전하게 전송하기 위해 HMAC 알고리즘으로 서명하거나 RSA, ECDSA와 같은 공개/개인키 알고리즘을 사용하여 디지털 서명을 할 수 있는 웹 토큰입니다. 서명된 토큰은 데이터의 무결성을 확인할 수 있고 포함된 정보를 다른 사용자로부터 숨길수 있습니다. 일반적으로 사용자가 로그인하면 JWT를 발급하고 사용자는 후속 요청부터는 헤더에 JWT를 포함하여 요청합니다. 이때 서버는 사용자의 정보를 확인하기 위해 Database 또는 Authentication Server에 조회하지 않고 JWT만 검증하여 사용자의 서비스 및 리소스 요청을 처리할 수 있기 때문에 오버헤드도 적습니다.
JWT 동작 방식
사용자가 로그인을 하면 서버가 JWT을 생성하여 사용자에게 전달하고 이후 요청시에 Authorization 헤더에 Bearer 스키마를 사용하여 JWT를 넣어서 요청합니다. 그러면 서버는 JWT 유효성을 검사하고 서비스 및 리소스를 제공하게 됩니다.
한가지 주의해야 될 점이 있는데 공식사이트 소개에는 “Authorization 헤더로 전송하면 쿠키를 사용하지 않기 때문에 CORS(Cross-Origin Resource Sharing) 문제가 발생하지 않는다“라고 나와있지만 브라우저에서 일반적인 요청이 아닐 경우에 Preflight Request라는 예비 요청을 보내 접근 권한이 있는지 확인하는데 이 과정에서 CORS가 발생할 수 있습니다. (자세한 내용은 CORS Preflight Request 발생원인과 해결를 참고하세요.)
JWT 장점
- 클라이언트에 JWT를 보관하기 때문에 서버의 자원을 절약하고 서버는 JWT만 검증하면 되기 때문에 오버헤드가 적습니다.
- JSON은 구조가 심플하기 때문에 용량도 작고 간결합니다. 그래서 HTTP 환경에서 전달하기 좋습니다.
- 서명을 위해 x.509 인증서 형식의 공개/개인 키 쌍을 사용할 수 있습니다.
- JSON Parser는 대부분 언어에서 지원하기 때문에 쉽게 JSON 문자열에서 객체로 변환할 수 있습니다.
- 여러 플랫폼에서 처리하기 용이합니다.
JWT 단점
- 데이터베이스의 값이 변경되어도 JWT는 클라이언트에 저장되므로 변경할 수 없습니다.
- 토큰 정보가 노출될 수 있기 때문에 저장할 수 있는 정보가 제약적이고 만료시간을 짧게 설정해야 됩니다.
- 요청 Header에 JWT를 포함하여 전송하기 때문에 네트워크 부하가 늘어날 수 있습니다.
JWT 구조
JWT 구조는 점(.)으로 구분된 Header, Payload, Signature로 구성된 Base64 URL 인코딩 문자열로 표현됩니다.
Header.Payload.Signature
Header
JWT 유형과 HS256(HMAC SHA256) 또는 RSA와 같은 사용할 알고리즘을 지정합니다.
{
"alg": "HS256",
"typ": "JWT"
}
Payload
일련의 클레임(정보)를 포함하고 있는 부분으로 클레임은 등록된 클레임(Registered Claim), 공개 클레임(Public Claim), 비공개 클레임(Private Claim) 3가지 종류가 있습니다.
- 등록된 클레임(Registered Claim) : 토큰 정보를 표현하기 위해 이미 정해진 종류의 데이터들로 iss (발급자), exp(만료 시간), sub(제목), aud(대상) 및 기타가 있고 모두 선택적으로 작성이 가능 합니다.
- 공개 클레임(Public Claim): 사용자 정의 이름을 사용할 수 있습니다. 다만 기존 등록된 클레임 이름과 충돌할 수 있기 때문에 IANA JSON Web Token Register에 정의하거나 또는URI 형식으로 사용해야 합니다.
- 비공개 클레임(Private Claim) : 당사자간 협의된 임의의 정보를 공유하기 위한 클레임입니다.
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
Signature(서명)
서명은 위변조 여부를 확인하는데 사용되며 개인키로 서명된 경우 보낸사람이 누구인지 확인도 가능합니다. 생성 방법은 Header와 Payload를 각각 Base64URL 인코딩하고 비밀키(서버만 알고 있는 임의 값)를 이용해 Header에 지정된 알고리즘으로 해싱한 다음 이 값을 다시 Base64URL 인코딩하여 생성합니다.
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
JWT 디버거
JWT 디버거에서 인코드, 디코드 결과를 볼 수 있는 기능을 제공합니다.
JWT 저장 방법
JWT 자체가 자격증명이므로 보안 문제에 신경을 써야되기 때문에 웹 스토리지에 저장할 때 장단점을 고려하여 선택해야 합니다.
저장 방식 | 장점 | 단점 |
---|---|---|
LocalStorage SessionStorage |
js코드에 의해 헤더에 포함되어 전송되므로 CSRF 공격에는 안전한 편입니다. | js를 조작하는 XSS(Cross-Site Scripting)에 취약합니다. |
Cookie | cookie에 httpOnly와 secure 사용하면 js 조작하더라도 cookie에서 토큰을 읽을 수 없기 때문에 XSS(Cross-Site Scripting) 공격에는 안전한 편입니다. | 공격자가 사용자에게 공격페이지의 link를 클릭하도록 유도하여 request를 위조하는 CSRF(Cross-site Request Forgery) 공격에 취약합니다. 그리고 cookie의 크기 제한이 4KB이기 때문에 큰 JWT 토큰을 사용할 수 없습니다 |
결론 | httpOnly과 secure을 적용하고 SameSite 라는 속성에 Strict나 Lax 속성을 준 쿠키를 사용하고 csrf 토큰 및 xss 방지 라이브러리를 사용한다면 좀 더 안전한 방법이 될 것으로 보입니다. |
댓글남기기