인가 서버
고친 사람 github-actions[bot]
인가 서버는 사람에게 허락을 묻는 서버입니다. 받아 낸 허락은 토큰 한 장으로 바꿔 앱에 내줍니다. 사진 편집 앱이 내 사진첩을 읽으려 할 때, 앱은 비밀번호 대신 이 토큰을 내밉니다. 데이터를 들고 있는 서버는 토큰만 보고 요청을 통과시킬지 정합니다.
쉽고 빠른 이해
인가 서버는 허락을 묻고 내주는 일만 따로 맡은 서버입니다. 사진 편집 앱이 내 사진첩을 읽으려 할 때, 앱 대신 나에게 사진 읽기를 허락할지 묻고 그 답을 토큰으로 바꿔 줍니다.
이게 없으면 앱마다 내 비밀번호를 받아 두어야 합니다. 비밀번호 하나가 새면 그 서비스에서 내가 할 수 있는 일이 전부 열립니다. 앱 하나만 끊으려 해도 비밀번호를 바꿔 나머지 앱까지 같이 끊어야 합니다.
어떻게 도나:
- 앱이 나를 인가 서버로 보냅니다
- 인가 서버가 내가 누구인지 확인하고 무엇을 허락할지 묻습니다
- 허락이 떨어지면 앱에 토큰을 내줍니다
허락이 한 군데로 모이는 것이 대가입니다. 이 서버가 멈추면 어느 앱도 새 토큰을 못 받습니다. 뚫리면 딸린 서비스가 함께 열립니다.
상세
호텔에 묵는 동안 친구가 내 방에 짐을 갖다 놓아 주기로 했다고 해 봅시다. 프런트는 내가 그 방 투숙객이 맞는지 확인하고, 어디까지 열어 줄지 내게 물은 뒤, 오늘 하루 그 방 문만 열리는 카드키를 한 장 더 만들어 친구에게 내줍니다. 객실 문은 그 카드가 맞는지만 보고 열립니다.
비밀번호를 그대로 넘길 때의 문제
남의 서비스에 있는 내 데이터를 다른 앱이 쓰게 하려면, 예전에는 그 앱에 아이디와 비밀번호를 그대로 넘겨야 했습니다. 넘기는 순간 그 앱은 내가 할 수 있는 일을 전부 할 수 있게 됩니다. 사진만 읽으면 되는 앱이 사진을 지울 수도 있습니다.
허락을 거두기도 어렵습니다. 앱 하나만 끊으려 해도 비밀번호를 바꿔야 합니다. 그러면 같은 비밀번호를 쥐고 있던 다른 앱까지 함께 끊깁니다.
그래서 허락을 묻고 내주는 일을 한 서버가 따로 맡습니다. 이 구도에는 네 쪽이 등장합니다. 인가 서버는 그중 허락을 다루는 쪽입니다. 네 이름은 OAuth(Open Authorization)라는 권한 위임 규격이 쓰는 말입니다.
| 이름 | 누구인가 |
|---|---|
| 자원 소유자 | 데이터의 임자입니다. 대개 사용자 본인입니다 |
| 클라이언트 | 그 데이터를 대신 쓰려는 앱입니다 |
| 자원 서버 | 데이터를 들고 있고 요청을 받는 서버입니다 |
| 인가 서버 | 허락을 받아 토큰으로 바꿔 내주는 서버입니다 |
앱이 받아 가는 것은 비밀번호가 아니라 액세스 토큰입니다. 이 문자열에는 무엇을 얼마 동안 해도 되는지가 미리 매여 있습니다. 사진 읽기만 허락했다면 그 토큰으로는 사진을 지울 수 없고, 기한이 지나면 저절로 통하지 않습니다.
사람에게 묻는 창구와 앱이 오는 창구
인가 서버는 성격이 다른 두 창구를 엽니다. 하나는 사람을 상대하고, 다른 하나는 앱을 상대합니다.
인가 엔드포인트는 사람이 브라우저로 찾아오는 창구입니다. 로그인 화면과 「이 앱에 사진 읽기를 허락할까요」를 묻는 동의 화면이 이 창구에서 뜹니다.
앱은 사람을 이 창구로 직접 데려가지 않습니다. 사람이 보고 있던 브라우저를 이 주소로 돌려보냅니다. 그러면서 누가 무엇을 청하는지를 주소에 실어 보냅니다.
GET /authorize // 사람이 오는 창구
?client_id=photo-app // 어느 앱인가
&scope=photo.read // 무엇을 하려는가
&redirect_uri=… // 끝나면 돌아갈 곳
주소에 실린 scope 가 무엇까지 허락받으려는지를 적은 목록입니다. 이 목록을 스코프라고
부릅니다. 동의 화면이 사람에게 보여 주는 것이 그 내용입니다.
사람이 허락을 누르면 인가 서버는 앱으로 돌려보내면서 짧은 증표를 하나 쥐여 줍니다. 그것이 인가 코드입니다. 한 번 쓰면 버려지는 값이라 오래 쓸 수 없습니다.
토큰 엔드포인트는 앱의 서버가 직접 찾아오는 창구입니다. 앱은 방금 받은 인가 코드와 자기 신분을 함께 내밀고 액세스 토큰을 받아 옵니다. 사람도 브라우저도 이 왕복에 끼지 않습니다.
두 창구를 나란히 놓으면 이렇습니다.
| 인가 엔드포인트 | 토큰 엔드포인트 | |
|---|---|---|
| 찾아오는 쪽 | 사람의 브라우저 | 앱의 서버 |
| 화면 | 로그인·동의 화면을 띄운다 | 없다 |
| 내주는 것 | 인가 코드 | 액세스 토큰 |
두 창구를 가르는 까닭은 오는 상대가 다르기 때문입니다. 브라우저를 지나는 길에는 주소창과 방문 기록이 남습니다. 오래 쓰는 토큰을 그 길로 보내지 않으려고, 한 번 쓰고 버릴 짧은 코드를 대신 태워 보냅니다.
토큰 한 장이 나오기까지
바로 앞에서 본 두 창구가 한 줄로 이어지는 모습입니다. 사진 편집 앱이 내 사진첩을 읽으려는 경우를 따라갑니다.
sequenceDiagram
participant 사용자
participant 앱
participant 인가서버 as 인가 서버
participant 자원서버 as 자원 서버
앱->>인가서버: 사용자를 보내며 사진 읽기를 청한다
인가서버->>사용자: 누구인지 확인하고 허락할지 묻는다
사용자-->>인가서버: 사진 읽기만 허락한다
인가서버-->>앱: 한 번 쓰고 버릴 인가 코드
앱->>인가서버: 인가 코드와 앱 자신의 신분을 낸다
인가서버-->>앱: 액세스 토큰
앱->>자원서버: 액세스 토큰을 내밀고 사진을 청한다
자원서버-->>앱: 사진
인가 서버와의 왕복이 둘로 나뉩니다. 앞은 사람에게 묻는 왕복이고, 뒤는 앱과 둘이서만 하는 왕복입니다. 사용자는 앞에서만 낍니다. 그다음부터는 앱이 토큰을 들고 일합니다.
인가 서버는 액세스 토큰과 함께 리프레시 토큰을 내주기도 합니다. 액세스 토큰의 기한이 끝나면 앱은 사람을 다시 부르지 않고 이 문자열로 새것을 받아 옵니다.
자원 서버와 갈라 두는 까닭
토큰을 내주는 쪽과 그 토큰을 보고 데이터를 내주는 쪽이 한 프로그램 안에 있어도 됩니다. 그래도 둘을 갈라 부르는 것은 맡은 일이 다르기 때문입니다.
인가 서버는 허락을 받아 두는 쪽입니다. 사람을 확인하고, 무엇까지 허락하는지 정하고, 그 결정을 토큰에 담습니다. 자원 서버는 그 결정을 지킵니다. 요청이 올 때마다 토큰을 보고 통과시킬지 막을지만 정합니다.
갈라 두면 자원 서버가 여럿이어도 허락은 한 군데서 관리합니다. 사진 서버와 연락처 서버가 따로 있어도 사용자는 한 곳에서 한 번 허락하면 됩니다. 앱을 끊는 것도 한 곳에서 끝납니다.
자원 서버가 요청마다 인가 서버에 물어보지는 않습니다. 인가 서버는 토큰을 내줄 때 자기만 만들 수 있는 표식을 하나 찍어 둡니다. 그 표식이 서명입니다. 표식을 찍는 데 쓰는 비밀값은 열쇠라고 부릅니다.
자원 서버는 그 표식이 맞는지만 봅니다. 인가 서버에 묻지 않고도 위조가 아님을 알 수 있습니다.
매번 묻지 않는 까닭은 둘입니다. 물을 때마다 응답이 늦어집니다. 인가 서버가 멈추는 순간 모든 요청이 함께 멈춥니다.
대가는 허락이 한 군데 모인다는 것입니다. 이 서버가 멈추면 새 토큰이 하나도 안 나옵니다. 토큰에 서명하는 열쇠가 새면 남이 토큰을 마음대로 만들어 낼 수 있습니다. 그래서 인가 서버는 단일 장애점으로 다룹니다.
세울 때와 안 세울 때
내 서비스가 내 사용자를 직접 로그인시키고 그 로그인이 내 서비스 안에서 끝난다면, 인가 서버를 따로 세울 이유가 적습니다. 로그인 결과를 세션에 담아 두는 쪽이 부품이 적습니다. 끊는 일도 서버 안에서 끝납니다.
바깥 앱에 내 사용자의 데이터를 내줘야 할 때 이 서버가 값을 합니다. 서비스가 여럿으로 갈라져 같은 사용자가 여러 곳을 오갈 때도 그렇습니다. 허락을 묻는 화면과 끊는 화면이 한 곳에 모입니다. 서비스마다 같은 것을 다시 만들지 않아도 됩니다.
세울 때는 어디까지 맡길지도 함께 정합니다. 누구인지 알아내는 일이 인증이고, 무엇을 해도 되는지 정하는 일이 인가입니다. 누구인지 확인하는 일을 이미 다른 로그인 시스템이 하고 있다면, 인가만 맡는 구성으로 세웁니다.
로그인까지 함께 맡기면 이름도 갈립니다. 그런 구성에서는 같은 서버를 인증 서버라고도 부릅니다. 가리키는 것은 같습니다.
관련 항목
인가 서버가 상대하는 참여자
클라이언트 · 자원 소유자 · 자원 서버 · 사용자 에이전트 · 브라우저 · 신뢰 당사자
인가 서버가 발급하는 자격증명
액세스 토큰 · 리프레시 토큰 · 인가 코드 · ID 토큰 · Bearer 토큰 · 불투명 토큰 · JWT · 토큰
인가 서버가 여는 창구
인가 엔드포인트 · 토큰 엔드포인트 · 토큰 인트로스펙션 · 토큰 폐기 · JWKS · 동의 화면
인가 서버를 정의하는 표준·프로토콜
OAuth 2.0 · OpenID Connect · SAML · PKCE · 인가 코드 그랜트 · 클라이언트 자격증명 그랜트 · 기기 인가 그랜트
인가 서버가 다루는 권한 개념
인가 · 인증 · 권한 · 스코프 · 동의 · 최소 권한 원칙 · 인증과 인가
인가 서버를 노리는 공격
토큰 탈취 · 인가 코드 가로채기 · 리다이렉트 변조 · 크로스 사이트 요청 위조 · 재전송 공격 · 중간자 공격
인가 서버를 대신하는 로그인 유지 수단
세션 · 세션 쿠키 · 싱글 사인온 · API 키 · 비밀번호 인증 · 다요소 인증
인가 서버가 겪는 운영 문제
단일 장애점 · 키 회전 · 토큰 만료 · 무효화 · 감사 로그 · 서명
인가 서버를 구현한 제품
Keycloak · Auth0 · Okta · Amazon Cognito · Spring Authorization Server
다른 이름: authorization server · auth server