JWT 란?
- Json Web Token (이하 JWT 또는 Token 또는 access token) 은 개방형 표준이며, Json 객체를 사용해서 Token 자체에 정보를 저장하는 Web Token 이다.
- JWT 속의 정보(데이터)는 디지털 서명을 통해 확인하고, HTTP 를 통해 JWT 을 전송하는 경우에는 Token 의 각 구성요소를 암호화하여 전송한다.
- JWT 발급 및 갱신은 Server 단에서 이뤄지며 JWT 를 전달받은 Client 는 해당 Token 을 지속적으로 활용하여 Sever 에 인증을 하기 때문에 Server 의 인증 절차 및 부하를 최소화 한다.
- JWT 는 3가지 구성요소로 이뤄져 있다. (각 요소별 구조 및 세부사항은 하단에서 확인)
장/단점
장점
- 크기: Json 코드 언어로 생성된 Token 은 용량이 작기 때문에 두 Entity 사이에서 매우 빠르게 전달된다.
- 제어: 접근 가능한 데이터, 권한 지속 시간, 로그인 시 수행 가능한 작업 등을 지정할 수 있다.
- 확장성: 중앙 인증 서버, 저장소(DB 등)에 대한 의존성이 없기 때문에 수평 확장에 유리하다.
- 용이성: Base 64 Safe Encoding 이기 때문에 URL, Cookie, Header 등 어떤 형태로도 사용 가능하다.
- 웹, 모바일 어느 곳에서 사용 가능하다.
- 인증 정보를 다른 곳에서도 사용 가능하다. (ex. OAuth)
단점
- 단일 키: JWT 은 단일 키를 이용하기 때문에 Token 이 유출되면 Server 는 진짜 사용자를 구분할 수 없다.
- 복잡성: 인증 방법이 복잡하기 때문에 인증 서버를 구현할 때 서명 알고리즘에 정통하지 않는다면 시스템을 위험에 빠지게 할 수 있다.
- Payload 의 정보가 많아지면 네트워크 사용량이 증가한다.
- 다른 사람이 Token 을 Decode 하면 Token 내부 정보를 확인할 수 있다.
- 때문에 Token 자체에는 민감한 정보를 넣지 않아야 한다.
- Token 을 탈취당할 경우에 대한 대처가 어렵다.
- Token 을 Server 에서 관리하지 않기 때문에 Token 을 탈취당한 경우, 강제 로그아웃이 불가능하다.
- Token 유효기간이 만료되기 전까지 Server 는 진짜 사용자와 Token 탈취다를 구분할 수 없다.
- 이를 대비하기 위해 Token 의 유효기간을 짧게 가져가고 refresh token(인증 절차에서 상세히 설명) 을 통해 token 을 재발급 하는 방식으로 많이 사용한다.
구성 요소
Header
- 일반적으로 쓰이는 암호화 알고리즘들은 SHA-2(HS256) 방식의 HMAC와 SHA-256(RS256) 방식의 RSA 서명이다. JWA(Json Web Algorithm) RFC 7518은 인가 및 암호화를 위해 더 많은 것을 도입하고 있다.
alg:
{
"alg" : "HS256", // Signature 를 해싱하기 위한 알고리즘 정보를 가진다.
// Signature 생성을 위해 어느 알고리즘을 사용할지를 식별한다.
// HS256는 이 토큰이 HMAC-SHA256를 사용하여 서명됨을 의미한다.
"typ" : "JWT" // typ: 토큰의 타입을 나타내며, 보통 JWT 를 사용한다.
}
Payload
- Payload 에는 일련의 클레임을 포함한다. JWT 사양은 토큰에 일반적으로 포함되는 표준 필드인 7개의 등록 클레임 이름(Registered Claim Names)을 정의한다.
- Token의 사용 목적에 따라 사용자 지정 클레임 또한 일반적으로 포함됩니다.
- 서버와 클라이언트가 주고받는, 시스템에서 실제로 사용될 정보에 대한 내용을 담고 있다.
- JWT 가 기본적으로 갖고 있는 키워드가 존재하고 원한다면 추가할 수도 있다.
registered claim
- 등록된 클레임은 토큰 정보를 표현하기 위해 이미 정해진 종류의 데이터로, 선택적으로 작성이 가능하다. (사용 권장)
public claim
- 공개 클레임은 사용자 정의 클래임으로, 공개용 정보를 위해 사용. 충돌 방지를 위해 URI 포맷을 이용
- https://www.iana.org 여기에 공용으로 미리 정의된 claim name 을 사용
private claim
- Server 와 Client 사이에 임의로 지정한 정보를 저장
{
// registered claim
"iss": "http://token.issuer.com", // 토큰 발급자
"sub": "test_user@test.com", // 토큰의 주제(고유)
"aud": "http://token.audience.com", // 토큰 대상자
"exp": 1652356540, // 토큰 만료시간으로, numericDate(unix timestamp) 형식으로 되어있어야 함
"nbf": 1652320540, // 토큰이 활성화 되는 날짜
"iat": 1652320540, // 토큰을 발급한 시간
"jti": "id6098364921", // 토큰 식별자(고유ID)
// public claim
"profile": "https://cloud.test.com/user/id602394", // 미리 정의된 공개용 claim name
"website": "https://website.test.com/" // 미리 정의된 공개용 claim name
// private claim
"userType": "user", // Server 와 Client 사이에 정의된 claim name
"token_type": "access" // Server 와 Client 사이에 정의된 claim name
}
Signature
- 토큰을 안전하게 확인한다. 서명은 Base64url 인코딩을 이용하여 헤더와 페이로드를 인코딩하고 이 둘을 점(.) 구분자로 함께 연결시킴으로써 계산된다.
- Base64url 인코딩은 base64 와 비슷하지만 각기 다른 영숫자를 사용하며 패딩(padding)은 제외한다.
- 해당 문자열은 그 뒤 헤더에 규정된 암호화 알고리즘(이번 경우에는 HMAC-SHA256)에 유입된다.
- 서버에서 토큰이 유효한지 검증하기 위한 문자열이다.
- Header + Payload + Secret Key 로 값을 생성하므로 데이터 변조 여부 판단이 가능하다.
- Secret Key 는 노출되지 않도록 서버에서 잘 관리가 필요하다.
HMAC-SHA256(
secret_key, // 토큰을 생성하기 위한 비밀 키
base64urlEncoding(header) + '.' +
base64urlEncoding(payload)
)
Access token & Refresh token
JWT(이하 access token) 역시 탈취자에 의해 access token 이 탈취되면 탈취자가 API 를 호출할 수 있다는 단점이 존재한다.
Session 의 경우 Session 저장소에서 탈취한 Session ID 를 삭제하면 되지만, JWT 는 Server 에서 관리하지 않기 때문에 access token 이 탈취당할 경우 속수무책으로 당할 수 밖에 없다. 때문에 access token 이 탈취되어도 피해가 최소화 되도록 access token 의 유효시간을 짧게 가져가고 access token 을 재발급할 수 있는 Refresh token 을 도입하는 방법이 있다.
access token 유효기간을 짧게 가져간다면, 사용자는 짧은 기간동안만 Resource 에 접근이 가능하고, access token 이 만료되었을 경우 사용자는 새로 로그인 하여 access token 을 새로 발급받아야 하는 번거로움이 생기는데, 이때 access token 을 재발급할 수 있는 refresh token 을 도입하여 refresh token 유효기간동안 사용자가 매번 로그인 하는 과정을 생략할 수 있다.
Refresh token
refresh token 은 목적이 Access token(JWT) 을 재발급 하기 위함이며, Refresh token 자체에는 사용자 정보가 없다. 그리고 Server 의 저장소에 각 사용자별 refresh token 값이 있으므로 어떤 사용자의 refresh token 인지 판단하기 용이하다.
Access token 을 탈취당했을 때, Server 측에서는 저장소에 저장된 refresh token 정보를 삭제하면 Access token 이 만료된 후 access token 재발급을 받지 못하도록 막을 수 있다. 이렇게 하면 사용자 resource 에 접근할 수 없도록 로그아웃 처리를 할 수 있다.
Refresh token 저장 장소
refresh token 은 Server 에서 별도의 저장소에 보관하는 것이 좋다.
Server 의 경우, NoSQL 이나 기타 DB 에 저장할 수 있다.
Client 의 경우, 브라우저로 예를 들면 Cookie 나 local storage 등 다양한 곳이 있지만, http-only 속성이 부여된 Cookie 에 저장하는 것을 권장한다.
이유는 http-only 속성이 부여된 Cookie 는 javascript 환경에서 접근할 수 없기 때문에 XSS 나 CSRF 가 발생 하더라도 refresh token 이 누출되지 않는다. 일반 Cookie 나 브라우저의 local storage 는 javascript 로 자유롭게 접근할 수 있기 때문에 보안 측면에서는 권장하지 않는다.
Token 재발급 시나리오
- Client 는 최초에 ID & PW 를 통해 access token 및 refresh token 을 발급 받는다.
- Client 는 Server 로 부터 발급 받은 access token 으로 API 를 호출하여 resource 접근 또는 service 를 제공 받는다.
- Client 가 만료된 access token 을 통해 Server 로 API 호출 시, Server 는 token 만료 응답을 내려준다.
- Client 는 token 만료 응답을 받고 access token 을 재발급 받기 위해 1. 에서 발급 받은 access token 과 refresh token 을 Server 로 전송한다.
- Server 는 access token 에 담긴 사용자 정보와 refresh token 의 유효성을 체크한다
5-1. refresh token 이 유효하다면 access token 을 새로 발급하여 응답한다.
5-2. refresh token 이 유효하지 않는다면 Client 는 ID & PW 로 로그인을 통해 access token 및 refresh token 을 새로 발급 받아야 한다.
5-3. access token 에 담긴 사용자와 전달 받은 refresh token 의 사용자가 일치하지 않는 경우에는 Server 는 저장소에 저장된 refresh token 을 폐기 후 ID & PW 로 로그인을 해야한다고 응답한다.
Token 탈취 이슈
Access token 의 탈취
access token 이 탈취자에 의해 탈취당했을 경우에 Server 는 탈취자와 진짜 사용자를 구분하기가 어렵다. 때문에 탈취자는 access token 의 유효기간 동안 Server 로 부터 API 를 호출 하거나 사용자 resource 에 접근이 가능하다. 이를 방지하기 위해 refresh token 을 도입하고 access token 의 유효기간을 짧게 가져간다.
Refresh token 의 탈취
refresh token 이 탈취자에 의해 탈취당했을 경우에는 탈취자가 refresh token 의 유효기간 만큼 다시 access token 을 재발급 받아 정상 사용자인 것 처럼 위장할 수 있다. 100% 완벽한 보안은 없기 때문에 Server 측의 검증 로직 강화가 필요하다.
참조
- https://velog.io/@primadonna/%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%9D%B8%EC%A6%9D%EB%B0%A9%EC%8B%9D%EC%9D%98-%EC%A2%85%EB%A5%98
- https://bcp0109.tistory.com/321
- https://velog.io/@park2348190/JWT%EC%97%90%EC%84%9C-Refresh-Token%EC%9D%80-%EC%99%9C-%ED%95%84%EC%9A%94%ED%95%9C%EA%B0%80
- https://velog.io/@cham/JWT-JWT-%EA%B3%A0%EC%B0%B0-SecurityJWT
'Backend > 인증' 카테고리의 다른 글
| [인증] OAuth 2.0 (개념 정리) (1) | 2022.10.22 |
|---|---|
| [인증] 사용자 인증 방식 종류 (0) | 2022.10.22 |