신뢰 당사자
고친 사람 github-actions[bot]
신뢰 당사자는 사용자가 누구인지 직접 확인하지 않고 남이 확인해 준 결과를 믿고 받아 쓰는 서비스입니다. 소셜 로그인 버튼을 단 쇼핑몰이 그렇습니다. 비밀번호는 로그인을 맡은 서비스가 받습니다. 쇼핑몰은 그쪽이 보내 준 확인서만 검사합니다. 남이 발급한 인증서를 믿고 쓰는 쪽도 같은 이름으로 부릅니다.
쉽고 빠른 이해
신뢰 당사자는 로그인을 남에게 맡기고 그 결과만 받아 쓰는 서비스입니다. 쇼핑몰에 「다른 계정으로 로그인」 버튼이 있다고 합시다. 비밀번호는 로그인을 맡은 서비스가 받습니다. 쇼핑몰은 「이 사람은 누구다」라는 확인서만 받습니다.
이렇게 하면 쇼핑몰이 비밀번호를 받지도 보관하지도 않아도 됩니다. 사용자도 서비스마다 새 비밀번호를 만들지 않아도 됩니다.
어떻게 도나:
- 쇼핑몰이 사용자를 로그인 담당 서비스로 보냅니다
- 로그인 담당 서비스가 사용자를 확인합니다
- 로그인 담당 서비스가 서명한 확인서를 돌려줍니다
- 쇼핑몰은 확인서의 서명과 받는 사람과 기한을 검사한 뒤 사용자를 들여보냅니다
대가는 로그인이 남에게 묶인다는 것입니다. 로그인 담당 서비스가 멈추면 새로 로그인할 수 없습니다. 쇼핑몰이 알 수 있는 사용자 정보도 그쪽이 내주는 만큼입니다.
상세
편의점 점원은 손님이 성인인지 직접 알아내지 않습니다. 나라가 만든 신분증을 믿습니다. 점원이 보는 것은 신분증이 진짜인지와 기한이 남았는지뿐입니다. 이 점원이 신뢰 당사자입니다.
이 절은 로그인 한 번을 따라갑니다. 신뢰 당사자가 무엇을 넘겨받아 검사하는지 봅니다. 끝으로 같은 이름이 로그인 밖에서 가리키는 것을 봅니다.
로그인을 남에게 맡기는 구도
서비스마다 비밀번호를 따로 받으면 두 쪽이 다 곤란합니다. 사용자는 서비스 수만큼 비밀번호를 만들어야 합니다. 서비스는 받은 비밀번호를 안전하게 보관할 책임을 떠안습니다. 한 곳에서 비밀번호가 새면, 같은 비밀번호를 쓰던 다른 서비스까지 위험해집니다.
해법은 사용자를 확인하는 일을 한 서비스에 모으는 것입니다. 이 서비스가 신원 제공자입니다. 영어로는 IdP(Identity Provider)라고 줄여 씁니다.
신원 제공자는 사용자 계정과 비밀번호를 쥐고 있습니다. 다른 서비스를 대신해 로그인을 받아 줍니다. 편의점 비유에서 신분증을 만든 나라가 이쪽입니다.
로그인을 맡기는 쪽이 신뢰 당사자입니다. 영어로는 RP(Relying Party)라고 줄여 씁니다. relying 은 「기댄다」는 뜻입니다. 자기가 확인하지 않은 결과에 기대어 사용자를 들여보낸다는 데서 붙은 이름입니다.
이 구도에 등장하는 셋을 표로 모으면 이렇습니다.
| 이름 | 하는 일 |
|---|---|
| 사용자 | 로그인하려는 사람입니다. 브라우저로 두 서비스 사이를 오갑니다 |
| 신원 제공자 | 사용자를 확인하고 「이 사람은 누구다」라는 확인서를 내줍니다 |
| 신뢰 당사자 | 그 확인서를 믿고 사용자를 자기 서비스에 들여보냅니다 |
신원 제공자가 내주는 확인서가 단언입니다. 편의점 비유의 신분증에 해당합니다. 단언에는 누가, 언제, 어느 서비스를 위해 로그인했는지가 적혀 있습니다.
신원 제공자는 단언에 서명을 붙여 내보냅니다. 서명은 신원 제공자만 만들 수 있는 표식입니다. 중간에서 누가 단언 내용을 바꾸면 서명이 맞지 않아 신뢰 당사자가 알아챕니다.
서명에는 짝을 이루는 키 두 개를 씁니다. 신원 제공자는 자기만 가진 개인 키로 표식을 만듭니다. 신뢰 당사자는 누구에게 알려도 되는 공개 키로 표식이 맞는지 봅니다.
단언이 오가는 순서
쇼핑몰이 신뢰 당사자이고 사용자가 로그인 버튼을 누른 경우를 따라갑니다. 등장하는 쪽은 앞의 표의 셋입니다.
sequenceDiagram
participant 사용자
participant 신뢰당사자 as 신뢰 당사자
participant 신원제공자 as 신원 제공자
사용자->>신뢰당사자: 로그인 버튼을 누른다
신뢰당사자-->>사용자: 신원 제공자로 가라고 돌려보낸다
사용자->>신원제공자: 비밀번호 등으로 로그인한다
신원제공자-->>사용자: 서명한 단언을 들려 돌려보낸다
사용자->>신뢰당사자: 단언을 전한다
Note over 신뢰당사자: 단언을 검사한다
신뢰당사자-->>사용자: 로그인 완료
그림에서 비밀번호는 신원 제공자에게만 갑니다. 신뢰 당사자가 받는 것은 서명된 단언 하나입니다.
사용자를 다른 서비스로 보내는 일은 브라우저를 다른 주소로 돌려보내는 방식으로 합니다. 이 방식이 리다이렉트입니다. 사용자가 보고 있던 화면이 신원 제공자의 로그인 화면으로 바뀝니다. 로그인이 끝나면 다시 쇼핑몰로 돌아옵니다.
단언을 넘겨받는 길은 규격마다 다릅니다. 한 길은 브라우저에 단언을 통째로 실어 보내는 것입니다.
다른 길은 브라우저에 짧은 교환용 코드만 실어 보내는 것입니다. 신뢰 당사자의 서버는 그 코드를 신원 제공자에게 내밉니다. 그러면 신원 제공자가 단언을 서버로 직접 돌려줍니다. 어느 쪽이든 마지막에 단언을 검사하는 쪽은 신뢰 당사자입니다.
검사를 통과하면 신뢰 당사자는 자기 세션을 만듭니다. 세션은 로그인한 사용자를 다음 요청에서도 알아보려고 서버가 쥐고 있는 기록입니다. 이때부터 사용자의 요청은 쇼핑몰의 세션으로 처리됩니다. 요청마다 신원 제공자에게 다시 묻지 않습니다.
미리 해 두는 등록
신원 제공자는 아무 서비스에나 단언을 내주지 않습니다. 신뢰 당사자는 먼저 신원 제공자에 자기를 등록합니다. 등록할 때 자기를 가리키는 식별자와 로그인이 끝나면 돌아올 주소를 적어 둡니다.
돌아올 주소를 미리 적어 두는 까닭은 단언이 엉뚱한 곳으로 새는 것을 막기 위해서입니다. 로그인 요청에 적힌 돌아갈 주소가 등록된 주소와 다르면 신원 제공자는 단언을 보내지 않습니다. 공격자가 주소를 자기 서버로 바꿔 끼워도 단언을 가로챌 수 없습니다.
넘겨받은 단언에서 검사하는 것
단언을 받았다고 바로 믿으면 안 됩니다. 신뢰 당사자는 몇 가지를 맞춰 본 뒤에 믿습니다. 하나라도 빠지면 그 틈으로 남의 단언이 통과합니다.
| 검사 | 확인하는 것 | 빠뜨리면 생기는 일 |
|---|---|---|
| 서명 | 신원 제공자가 만든 것이 맞나 | 누구나 단언을 지어내 로그인합니다 |
| 발급자 | 내가 믿기로 한 신원 제공자가 냈나 | 다른 발급처가 만든 단언이 통과합니다 |
| 받는 쪽 | 나를 위해 발급된 것인가 | 다른 서비스용 단언을 내 서비스에 내밉니다 |
| 유효 기간 | 기한이 안 지났나 | 오래전에 빼낸 단언으로 로그인합니다 |
| 일회용 값 | 내가 보낸 요청에 대한 답인가 | 한 번 쓴 단언을 다시 보냅니다 |
표에서 놓치기 쉬운 것은 받는 쪽 검사입니다. 서명이 맞으면 진짜 단언이라고 믿기 쉽습니다. 그런데 같은 신원 제공자를 쓰는 다른 서비스가 받은 단언도 서명은 맞습니다. 받는 쪽 칸에 내 식별자가 적혀 있는지를 봐야 그 단언이 나에게 온 것임을 압니다.
일회용 값은 신뢰 당사자가 로그인 요청마다 새로 만들어 보내는 임의의 문자열입니다. 앞에서 본 교환용 코드와는 다른 값입니다. 신원 제공자는 이 값을 단언에 그대로 적어 돌려줍니다.
신뢰 당사자는 자기가 보낸 값과 돌아온 값이 같은지 봅니다. 한 번 맞춰 본 값은 지워 둡니다. 같은 단언을 누가 다시 보내면 맞춰 볼 값이 이미 없어서 걸러집니다. 이렇게 한 번 쓴 단언을 다시 보내는 공격이 재전송 공격입니다.
아래는 로그인 규격 OpenID Connect 에서 신뢰 당사자가 받는 ID 토큰의 내용 일부입니다. ID 토큰이 이 규격의 단언입니다. 오른쪽 주석이 위 표의 어느 검사에 쓰이는 값인지 적습니다.
{
"iss": "https://idp.example", // 발급자
"aud": "shop-app", // 받는 쪽
"exp": 1767225600, // 유효 기간
"nonce": "n-0S6WzA2Mj", // 일회용 값
"sub": "248289761001" // 사용자
}
필드 이름은 영어 낱말의 줄임입니다. iss 는 issuer(발급자), aud 는 audience(받는 쪽), exp 는 expiration(만료), sub 는 subject(주체)를 줄인 것입니다.
sub 는 신원 제공자가 이 사용자에게 붙인 고유 번호입니다. 신뢰 당사자는 이 번호로 자기 회원을 찾습니다. 발급자가 다르면 같은 번호가 다른 사람일 수 있습니다. 그래서 회원을 찾을 때는 발급자와 이 번호를 한 쌍으로 씁니다.
규격마다 부르는 이름
로그인을 맡기는 규격은 여럿입니다. 규격마다 같은 역할을 다른 이름으로 부릅니다. 흔히 만나는 규격은 둘입니다.
SAML(Security Assertion Markup Language)은 기업 안의 로그인에 오래 쓰여 온 규격입니다. 사내의 여러 업무 시스템에 한 계정으로 들어가게 할 때 자주 만납니다.
OpenID Connect 는 웹과 모바일 앱의 로그인에 널리 쓰이는 규격입니다. 이 규격은 OAuth(Open Authorization)라는 다른 규격 위에 로그인 기능을 얹어 만들었습니다.
OAuth 는 로그인이 아니라 권한을 다루는 규격입니다. 사용자가 허락하면 앱이 사용자 대신 다른 서비스를 부를 수 있게 해 줍니다.
OAuth 에서 권한 증표를 내주는 쪽이 인가 서버입니다. 그 증표가 액세스 토큰입니다. 앱은 액세스 토큰을 내밀어 사용자 대신 다른 서비스를 부릅니다.
| 규격 | 신뢰 당사자를 부르는 이름 | 신원 제공자를 부르는 이름 | 단언 |
|---|---|---|---|
| SAML | Service Provider | Identity Provider | SAML 단언 |
| OpenID Connect | Relying Party | OpenID Provider | ID 토큰 |
| OAuth | 클라이언트 | 인가 서버 | 액세스 토큰 |
표의 마지막 줄은 로그인이 아니라 권한을 다룹니다. 그래도 OpenID Connect 의 신뢰 당사자는 OAuth 로 보면 클라이언트입니다. OpenID Connect 가 OAuth 위에 얹힌 규격이기 때문입니다. OpenID Connect 문서를 읽다 보면 두 이름이 한 대상을 번갈아 가리킵니다.
디지털 신원 모델에서는 역할을 한 칸 더 잘게 나눕니다. 신원 제공자가 하던 일 가운데 비밀번호 같은 인증 수단을 검사하는 부분을 떼어 낸 것이 검증자입니다. 단언에 담기는 것은 검증자가 내린 검사 결과입니다. 신뢰 당사자는 그 결과에 기대어 사용자를 들여보냅니다.
여러 신뢰 당사자가 한 신원 제공자의 로그인을 함께 믿는 구성이 연합입니다. 신뢰 당사자들은 사용자를 저마다 확인하지 않습니다. 같은 신원 제공자가 내준 단언을 받아 씁니다.
이 구성에서는 신원 제공자에 한 번 로그인하면 다른 신뢰 당사자에도 비밀번호 없이 들어갑니다. 한 번의 로그인으로 여러 서비스에 들어가는 이 방식을 SSO(Single Sign-On, 통합 로그인)라고 합니다.
인증서와 패스키에서의 신뢰 당사자
신뢰 당사자라는 이름은 로그인 밖에서도 씁니다. 가리키는 뜻은 같습니다. 남이 서명해 보증한 것을 직접 확인하는 대신 그 서명을 검사해 믿는 쪽입니다.
인증서는 「이 공개 키의 주인은 이 도메인이다」를 적고 서명한 문서입니다. 서명하는 쪽은 인증 기관이라는 보증 기관입니다.
브라우저가 HTTPS(HyperText Transfer Protocol Secure) 사이트에 접속하면 사이트가 인증서를 내밉니다. 브라우저는 인증 기관의 서명을 검사하고 그 사이트를 믿습니다. 이때 브라우저가 신뢰 당사자입니다.
패스키는 비밀번호 대신 기기 안에 든 개인 키로 로그인하는 방식입니다. 로그인할 때 기기가 개인 키로 서명합니다. 웹사이트는 가입 때 받아 둔 공개 키로 그 서명을 검사합니다.
웹에서 이 방식을 쓰게 해 주는 규격이 WebAuthn(Web Authentication)입니다. 이 규격에서는 로그인을 받는 웹사이트를 신뢰 당사자라고 부릅니다. 패스키는 만들 때 그 웹사이트의 도메인에 묶입니다. 그래서 생김새만 비슷한 가짜 사이트에서는 패스키가 쓰이지 않습니다.
세 쓰임을 나란히 놓으면 이렇습니다.
| 분야 | 신뢰 당사자 | 믿는 대상 | 보증하는 쪽 |
|---|---|---|---|
| 로그인 위임 | 로그인을 맡긴 서비스 | 단언 | 신원 제공자 |
| 인증서 | 인증서를 검사하는 브라우저나 프로그램 | 인증서 | 인증 기관 |
| 패스키 | 로그인을 받는 웹사이트 | 사용자 기기의 서명 | 사용자의 기기 |
로그인을 맡기는 대가
신뢰 당사자가 되면 로그인이 신원 제공자에 묶입니다. 신원 제공자가 멈추면 새로 로그인하려는 사용자는 들어올 수 없습니다. 이미 로그인해 세션을 받은 사용자는 세션이 끝날 때까지 그대로 씁니다.
알 수 있는 사용자 정보도 신원 제공자가 내주는 만큼입니다. 이메일이나 이름을 받으려면 로그인 요청에서 그 정보를 달라고 청해야 합니다. 사용자는 그 요청을 거절할 수 있습니다.
로그아웃도 두 군데로 갈립니다. 신뢰 당사자의 세션과 신원 제공자의 세션은 따로 삽니다. 사용자가 신원 제공자에서 로그아웃해도 쇼핑몰의 세션은 남아 있습니다. 둘을 함께 끊으려면 서로 로그아웃을 알리는 절차를 더 붙여야 합니다.
로그인을 맡길 때와 직접 받을 때
로그인을 맡기면 직접 만들 부분이 줄어듭니다. 맡기는 편이 나은 경우는 이렇습니다.
- 사용자가 이미 가진 다른 서비스의 계정으로 가입 절차를 줄이고 싶을 때
- 회사 안의 여러 서비스에 직원이 한 계정으로 들어가야 할 때
- 비밀번호를 보관하는 책임을 지고 싶지 않을 때
반대로 로그인을 바깥 서비스에 묶을 수 없으면 직접 받습니다.
- 외부 연결 없이 돌아가야 하는 사내 시스템일 때
- 사용자가 다른 계정을 갖고 있다고 기대할 수 없을 때
이때는 서비스가 스스로 인증을 합니다. 비밀번호도 직접 보관합니다.
관련 항목
신뢰 당사자와 함께 로그인 구도를 이루는 참여자
신원 제공자 · 검증자 · 인가 서버 · 인증 서버 · 클라이언트 · 자원 서버 · 자원 소유자 · 사용자 에이전트 · 브라우저 · 자격증명 서비스 제공자
신뢰 당사자가 넘겨받아 검사하는 증표
단언 · ID 토큰 · SAML 단언 · JWT · 액세스 토큰 · 인증서 · 클레임 · 자격증명
신뢰 당사자의 역할을 정의하는 표준
OpenID Connect · SAML · OAuth 2.0 · WebAuthn · FIDO2 · X.509
신뢰 당사자가 단언을 믿기 전에 쓰는 검사 수단
전자 서명 · 공개 키 · 공개 키 기반구조 · 인증 기관 · JWKS · nonce · 리다이렉트 URI
신뢰 당사자를 여럿 묶는 로그인 방식
연합 인증 · SSO · 소셜 로그인 · 패스키 · 디지털 신원 · 세션
신뢰 당사자가 다루는 인증·인가 개념
인증 · 인가 · 인증과 인가 · 신원 · 신뢰 · 제로 트러스트
신뢰 당사자를 노리는 공격
다른 이름: relying party · Relying Party · RP · 의존 당사자