클레임
고친 사람 github-actions[bot]
클레임은 한 사용자에 대해 발급한 쪽이 보증하는 정보 한 가지를 다른 서버에 전해 줍니다. 로그인을 맡은 서버가 클레임 여러 개를 토큰에 담고 서명을 붙여 내줍니다. 그 토큰을 받은 서버는 사용자 정보를 다시 찾아보지 않고 그 내용을 믿고 씁니다. 쿠버네티스에서 저장 공간을 달라고 적어 내는 볼륨 클레임은 이름만 같은 다른 말입니다.
쉽고 빠른 이해
무슨 일을 하는 물건인가 — 사용자에 대한 정보 한 줄을 서버에서 서버로 옮깁니다. 로그인 서버가 「이 사람의 사용자 번호는 248289761001」, 「이 사람은 편집자 그룹」이라고 적어 주면 그 한 줄 한 줄이 클레임입니다.
왜 이렇게 하나 — 클레임이 없으면 요청을 받은 서버마다 「이 사람이 누구고 무엇을 해도 되나」를 사용자 저장소에 매번 물어야 합니다. 서비스가 여럿이면 그 질문이 한곳에 몰립니다. 믿을 만한 곳이 한 번 적어 준 것을 들고 다니면 그 질문을 건너뜁니다.
어떻게 도나
- 사용자가 로그인 서버에서 로그인합니다
- 로그인 서버가 그 사용자에 대한 클레임을 모아 토큰에 담고 서명합니다
- 토큰을 받은 서버가 서명을 확인한 뒤 클레임을 읽고 허락할지 정합니다
대가 — 한 번 내준 클레임은 토큰이 만료될 때까지 바뀌지 않습니다. 그 사이 권한을 빼앗아도 옛 클레임이 계속 통합니다. 서명은 내용을 숨기지 않으므로 토큰을 손에 넣은 사람은 누구나 클레임을 읽습니다.
상세
이 절은 클레임이 무엇이고 받는 쪽이 왜 그것을 믿을 수 있는지를 다룹니다. 신분증 비유로 시작합니다. 그다음 로그인 토큰 안에 든 클레임 한 벌을 실물로 풀어 봅니다.
신분증으로 보는 클레임
편의점 직원이 손님의 나이를 확인할 때 신분증을 봅니다. 직원은 주민센터에 전화를 걸지 않습니다. 카드에 적힌 생년월일을 믿는 까닭은 그 카드를 나라가 발급했고 위조하기 어렵게 만들어 두었기 때문입니다.
클레임은 신분증에 적힌 칸 하나에 해당합니다. 그 칸의 내용은 카드를 내민 사람이 한 말이 아니라 발급한 쪽이 한 말입니다. 받는 쪽은 발급한 쪽을 믿기 때문에 그 내용을 믿습니다.
주장이라는 이름
영어 claim 은 「주장」이라는 뜻입니다. 일상에서 「클레임을 건다」고 할 때의 항의와는 거리가 멉니다. 클레임은 누군가가 어떤 대상에 대해 내건 주장 한 줄입니다.
이름에 「주장」이 들어간 까닭은 클레임이 그 자체로는 참이라는 보장이 없기 때문입니다. 누구든 「나는 관리자다」라고 적을 수는 있습니다. 그 주장을 믿을지는 받는 쪽이 누가 적었는지를 보고 정합니다.
클레임 한 줄의 모양
클레임은 이름과 값의 짝으로 적습니다. 이름은 무엇에 대한 정보인지를 말합니다. 값은 그 내용을 담습니다. 여러 클레임은 흔히 JSON(JavaScript Object Notation, 자바스크립트 객체 표기법) 객체 하나에 모아 적습니다.
이렇게 모은 클레임에 서명을 붙여 한 줄 문자열로 만든 것이 JWT(JSON Web Token, JSON 웹 토큰)입니다. 로그인한 사용자를 알리는 토큰은 대개 이 형식을 씁니다.
서명은 서명에 쓴 키를 가진 쪽만 만들 수 있는 값입니다. 내용이 한 글자라도 바뀌면 서명이 맞지 않습니다. 그래서 받는 쪽은 서명을 보고 내용이 고쳐졌는지 알아챕니다.
아래는 로그인 서버가 내준 JWT 의 내용 부분을 풀어 본 것입니다. 한 줄이 클레임 하나입니다.
{
"iss": "https://id.example.com",
"sub": "248289761001", // 사용자 번호
"aud": "wiki-app", // 받을 서비스
"exp": 1781000600, // 만료 시각
"email": "[email protected]",
"groups": ["editor"] // 소속 그룹
}
첫 줄의 iss 는 이 클레임들을 누가 적었는지를 가리킵니다. 클레임을 적어 내주는 쪽을 발급자라고 부릅니다. 이 예에서는 https://id.example.com 이 발급자입니다.
sub 는 이 토큰이 누구에 대한 것인지를 가리킵니다. 클레임이 설명하는 대상을 주체라고 부릅니다. sub 에는 주체를 가리키는 번호가 들어갑니다. 한 발급자 안에서 사람마다 하나씩 붙고 바뀌지 않는 번호입니다.
email 과 groups 는 사용자에 대한 정보 그 자체입니다. 받는 서버가 화면에 이메일을 띄우거나 권한을 가를 때 읽는 것은 대개 이런 클레임입니다. aud 와 exp 는 다음 소절에서 봅니다.
받는 서버가 믿기 전에 보는 것
받은 클레임을 곧바로 쓰면 안 됩니다. JSON 한 덩이는 누구나 만들 수 있기 때문입니다. 아래 그림은 토큰이 오가는 순서와 받는 서버가 확인하는 때를 보여 줍니다.
sequenceDiagram
participant 사용자
participant 로그인 as 로그인 서버
participant 받는 as 받는 서버
사용자->>로그인: 로그인한다
로그인-->>사용자: 클레임을 담아 서명한 토큰
사용자->>받는: 요청에 토큰을 실어 보낸다
Note over 받는: 서명 · 발급자(iss) · 받을 서비스(aud) · 만료(exp)를 확인
받는-->>사용자: 클레임을 보고 허락하거나 거절
확인의 첫째는 서명입니다. 받는 서버는 키를 써서 서명이 맞는지 봅니다. 맞지 않으면 내용이 바뀌었거나 다른 키로 만든 토큰입니다.
서명이 맞으면 「이 키를 가진 쪽이 적었다」까지 알 수 있습니다. 그 쪽이 내가 믿기로 한 발급자인지는 iss 를 보고 따로 가립니다. 받을 서비스와 만료 시각도 클레임 자신을 보고 확인합니다.
| 확인 | 보는 것 | 막는 것 |
|---|---|---|
| 서명이 맞나 | 토큰의 서명 | 중간에서 내용을 고친 토큰 |
| 믿는 발급자인가 | iss |
모르는 서버가 적어 준 클레임 |
| 받을 서비스가 나인가 | aud |
다른 서비스에 가야 할 토큰을 가로채 들이미는 것 |
| 아직 유효한가 | exp |
오래전에 새어 나간 토큰 |
넷 중 하나라도 어긋나면 받는 서버는 클레임 전부를 버립니다. 특히 aud 를 건너뛰면 다른 서비스용으로 발급된 토큰이 이 서비스에서도 통하게 됩니다.
클레임 이름의 세 부류
클레임 이름은 누구나 지어 붙일 수 있습니다. 그러면 두 발급자가 같은 이름을 다른 뜻으로 쓰는 일이 생깁니다. JWT 는 이 충돌을 다루려고 이름을 세 부류로 나눕니다.
| 부류 | 이름을 정하는 방식 | 예 |
|---|---|---|
| 등록 클레임 | JWT 규격이 처음부터 정해 두어 뜻이 모두에게 같습니다 | iss · sub · aud · exp · nbf · iat · jti |
| 공개 클레임 | 규격 밖에서 새로 지은 이름을 남과 안 겹치게 만들어 씁니다 | https://id.example.com/department |
| 사적 클레임 | 주고받는 두 쪽이 합의해서 씁니다 | groups · tenant_id |
표의 등록 클레임 가운데 넷은 앞에서 봤습니다. 남은 셋은 토큰의 시각과 번호를 적습니다.
nbf— 이 시각 전에는 받지 말라는 시각입니다iat— 발급한 시각입니다jti— 토큰마다 붙는 고유 번호입니다. 같은 토큰을 두 번 쓰는 것을 가려내는 데 씁니다
공개 클레임은 등록 클레임 일곱 밖에서 누군가 새로 지은 이름입니다. 남과 겹치지 않게 하려고 이름을 등록부에 올리거나, 자기 도메인처럼 남이 못 쓰는 문자열을 이름에 넣습니다. 표의 예는 발급자 도메인을 앞에 붙인 이름입니다.
사적 클레임은 편하지만 이름이 겹칠 위험을 쓰는 쪽이 스스로 집니다. 여러 발급자의 토큰을 받는 서버에서는 같은 groups 가 발급자마다 다른 뜻일 수 있습니다.
인증과 인가에서 쓰이는 클레임
클레임은 두 질문에 답하는 데 쓰입니다. 「이 사람이 누구인가」와 「이 사람이 무엇을 해도 되나」입니다.
누구인지 확인하는 일이 인증입니다. 로그인 규격인 OpenID Connect 로 로그인하면 앱은 ID 토큰을 받습니다. ID 토큰은 방금 로그인한 사용자에 대한 클레임을 담은 토큰입니다. 앱은 그 안의 iss 와 sub 의 짝으로 자기 데이터베이스의 사용자를 알아봅니다.
OpenID Connect 는 사용자 정보를 담는 클레임 이름도 따로 정해 둡니다. name 과 email 이 그런 이름입니다. 이 규격을 따르는 발급자끼리는 이메일을 늘 같은 이름으로 적습니다.
무엇을 해도 되는지 정하는 일이 인가입니다. 서비스에 요청할 때는 액세스 토큰을 내밉니다. 액세스 토큰은 이 서비스를 써도 된다는 허락을 담은 토큰입니다.
액세스 토큰에는 허락받은 범위를 적은 스코프 클레임이 흔히 들어갑니다. 읽기만 허락받았는지, 쓰기까지 허락받았는지 같은 범위입니다. 받는 서버는 그 값을 보고 요청을 통과시키거나 막습니다.
사용자의 역할을 적은 클레임을 넣기도 합니다. 「편집자」나 「관리자」 같은 값입니다. 받는 서버가 이 역할을 보고 허락을 정하면 역할 기반 접근 제어가 됩니다.
부서나 보안 등급처럼 사용자에게 붙은 특징을 속성이라고 부릅니다. 받는 서버가 이런 속성을 조건에 넣어 허락을 정하면 속성 기반 접근 제어가 됩니다. 이때 클레임은 속성을 받는 서버까지 실어 나릅니다.
SAML(Security Assertion Markup Language, 보안 단언 마크업 언어)이라는 기업용 로그인 규격에서는 클레임 한 줄을 attribute 라고 부릅니다. 발급자가 내주는 문서인 단언 안에 이런 attribute 를 여럿 담습니다. 이름만 다를 뿐 발급자가 보증한 사용자 정보 한 줄이라는 점은 같습니다.
클레임을 쓰면 나빠지는 것
클레임을 토큰에 담아 보내면 받는 서버가 물어볼 일이 줄어듭니다. 그 대가로 세 가지가 나빠집니다. 셋은 모두 「한 번 적어 내준 것을 사용자가 들고 다닌다」에서 나옵니다.
| 나빠지는 것 | 왜 그런가 | 흔히 하는 대처 |
|---|---|---|
| 변경이 늦게 반영된다 | 이미 나간 토큰의 클레임은 고칠 수 없다 | 토큰 수명을 짧게 둔다 |
| 누구나 읽는다 | 서명은 내용을 숨기지 않는다 | 비밀인 값은 클레임에 넣지 않는다 |
| 요청이 무거워진다 | 요청마다 클레임 전부를 실어 보낸다 | 꼭 필요한 클레임만 담는다 |
변경이 늦는 문제는 이렇게 나타납니다. 관리자 권한을 빼앗아도 이미 받은 토큰에는 여전히 「관리자」라고 적혀 있습니다. 그 토큰이 만료될 때까지는 관리자 요청이 통합니다.
그래서 토큰 수명을 몇 분에서 몇 시간 정도로 짧게 둡니다. 토큰이 만료되면 리프레시 토큰으로 새 토큰을 받습니다. 리프레시 토큰은 사용자를 다시 로그인시키지 않고 토큰을 새로 발급받는 데 쓰는 별도의 토큰입니다. 새로 받을 때마다 클레임도 새로 적히므로 바뀐 권한이 그때 반영됩니다.
누구나 읽는 문제는 JWT 의 생김새에서 옵니다. 내용 부분은 base64url 로 인코딩되어 있을 뿐입니다. 인코딩은 글자를 바꿔 적는 방법이라 누구나 되돌려 읽을 수 있습니다. 내용을 숨기려면 서명과 별도로 암호화를 해야 합니다.
클레임이 맞는 경우와 안 맞는 경우
클레임은 서비스가 여럿이고 그 서비스들이 로그인 서버 하나를 함께 믿을 때 값을 합니다. 서비스마다 사용자 저장소를 뒤지지 않아도 됩니다. 로그인 서버가 잠깐 느려도 이미 받은 토큰으로 요청을 처리할 수 있습니다.
권한이 바뀌면 바로 반영되어야 하는 서비스라면 사정이 다릅니다. 이때는 토큰에 클레임을 담지 않고 아무 뜻 없는 문자열만 내주는 불투명 토큰을 씁니다. 받는 서버는 요청마다 발급자에게 그 문자열이 아직 유효한지와 누구의 것인지 묻습니다. 이렇게 묻는 절차를 토큰 인트로스펙션이라고 합니다.
서버가 하나뿐인 서비스도 클레임을 들고 다닐 이유가 적습니다. 로그인 상태를 서버 쪽 세션에 두고 사용자에게는 세션 ID만 주면 됩니다. 로그아웃이나 권한 변경은 서버의 세션을 지우거나 고치면 곧바로 반영됩니다.
이름이 같은 다른 클레임
쿠버네티스에서도 클레임이라는 말을 씁니다. 애플리케이션이 쓸 저장 공간을 「이만큼 달라」고 적어 내는 요청서를 볼륨 클레임이라고 부릅니다. 여기서 claim 은 주장이 아니라 「제 몫을 청구한다」는 뜻입니다. 사용자 신원과는 관계가 없습니다.
관련 항목
클레임을 담아 나르는 토큰과 규격
JWT · ID 토큰 · 액세스 토큰 · 리프레시 토큰 · 토큰 · OpenID Connect · OAuth 2.0 · SAML · 클레임 세트 · Base64
클레임 한 벌을 이루는 구성 요소
주체 · 발급자 · 등록 클레임 · 공개 클레임 · 사적 클레임 · 표준 클레임 · 클레임 유형
클레임을 믿게 해 주는 검증 장치
전자 서명 · JWS · JWE · JOSE 헤더 · JWKS · 공개 키 암호
클레임을 재료로 삼는 인증·인가 방식
인증 · 인가 · 인증과 인가 · 접근 제어 · 역할 기반 접근 제어 · 속성 기반 접근 제어 · 스코프 · 속성
클레임을 발급하고 받는 신원 체계의 구성 요소
신원 제공자 · 클레임 제공자 · 보안 토큰 서비스 · 단언 · 클레임 기반 신원 · 싱글 사인온 · 페더레이션
클레임을 들고 다니는 대신 쓰는 확인 방식
불투명 토큰 · 토큰 인트로스펙션 · 세션 · 세션 ID
이름이 겹치는 헷갈리는 이웃
PersistentVolumeClaim · 퍼시스턴트 볼륨 · Kubernetes
다른 이름: claim · claims