OAuth란 무엇인가요?
Jinhua Luo
November 18, 2022
다양한 웹사이트에 대한 새로운 계정을 만드는 것은 항상 번거로운 일이었습니다. 대부분의 경우 이름과 전화번호와 같은 동일한 사용자 정보를 포함하고 있기 때문에 중복 작업이 많습니다.
"비밀번호를 제공하지 않고도 앱이 내 데이터에 접근할 수 있도록 허용할 수 있을까요?"
OAuth는 이러한 문제를 해결하기 위해 중앙 집중식 권한 부여를 제공하는 표준입니다.
OAuth는 "Open Authorization"의 약자로, 액세스 위임을 위한 개방형 표준입니다. 이를 통해 사용자(리소스 소유자)는 비밀번호를 노출하지 않고도 애플리케이션과 웹사이트 간에 정보를 공유할 수 있습니다. OAuth는 널리 사용되며, 여러분도 매일 OAuth 서비스를 사용하고 있을 가능성이 높습니다. 예를 들어, GeeksforGeek에 로그인할 때 Google 계정을 사용하여 로그인할 수 있습니다. 이렇게 하면 GeeksforGeek이 Google 계정의 사용자 이름, 프로필 사진 등과 같은 일부 정보에 접근할 수 있도록 승인하게 됩니다.
OAuth의 역사
OAuth 1.0 프로토콜은 2010년 4월 RFC 5849로 발표되었으며, 정보 제공을 위한 요청(Request for Comments)입니다.
OAuth 2.0 프로토콜은 2012년 10월 RFC 6749로 발표되었으며, OAuth 2.0 Bearer Token Usage는 RFC 6750로 발표되었습니다.
OAuth 1.0 배포 경험을 바탕으로 구축되었지만, OAuth 2.0은 OAuth 1.0을 완전히 재작성한 것으로, 전체 목표와 일반적인 사용자 경험만 공유합니다. OAuth 2.0은 OAuth 1.0과 하위 호환되지 않습니다.
OAuth 2.0의 작동 방식
OAuth 프로토콜에서는 리소스 소유자의 사용자 이름과 비밀번호를 사용하여 보호된 리소스에 접근하는 대신, 클라이언트가 액세스 토큰을 사용합니다. 클라이언트는 리소스 소유자의 승인을 받아 권한 서버로부터 액세스 토큰을 얻습니다. 리소스 소유자가 클라이언트에게 권한을 부여하려면 먼저 애플리케이션에서 인증을 받아야 합니다. 그리고 인증은 리소스 소유자와 애플리케이션 간에만 이루어지기 때문에, 제3자 클라이언트는 리소스 소유자의 개인 정보를 알 수 없습니다.
OAuth 프로토콜을 구현하면 제3자 클라이언트의 인증 프로세스가 크게 단순화됩니다. 클라이언트가 해야 할 일은 사용자로부터 권한을 얻고, 액세스 토큰을 요청하고, 이를 사용하여 사용자 정보(보호된 리소스)를 얻는 것입니다. 새로운 사용자가 계정을 등록하거나 자격 증명을 노출할 필요가 없으므로 공격 표면이 줄어들고 네트워크 보안이 강화됩니다.
OAuth는 인증이 아니라는 점에 유의해야 합니다. 여기서 Auth는 권한 부여를 의미합니다. 사용자는 애플리케이션에 로그인하지 않습니다. 단지 제3자 애플리케이션이 자신의 일부 정보를 얻을 수 있도록 승인할 뿐입니다.
OAuth의 권한 부여 프로세스
역할
OAuth는 네 가지 역할을 정의합니다:
클라이언트 - 리소스 소유자의 데이터에 접근하고, 리소스 소유자의 권한을 받아 보호된 리소스 요청을 하고자 하는 애플리케이션입니다.
리소스 소유자 - 리소스 서버에 있는 데이터를 소유하고, 클라이언트의 서비스를 사용하고자 하며, 권한 서버에 계정이 있는 사용자입니다. 예를 들어, 저는 Facebook 프로필의 리소스 소유자이며, GeeksforGeeks의 서비스를 사용하고 싶습니다.
권한 서버 - OAuth의 핵심 엔진입니다. 리소스 소유자를 성공적으로 인증하고 권한을 얻은 후 클라이언트에게 액세스 토큰을 발급합니다.
리소스 서버 - 클라이언트가 원하는 데이터를 저장하고, 액세스 토큰을 사용하여 보호된 리소스 요청을 수락하고 응답할 수 있는 서버입니다.
OAuth 프로토콜 흐름
단계 A: 제3자 애플리케이션이 사용자에게 권한 요청
단계 B: 제3자 애플리케이션이 리소스 소유자의 권한을 나타내는 자격 증명인 권한 부여를 받음
단계 C: 제3자 애플리케이션이 권한 부여를 사용하여 액세스 토큰 요청
단계 D: 권한 서버가 클라이언트를 인증하고 권한 부여를 검증한 후, 유효한 경우 제3자 애플리케이션에 액세스 토큰 발급
단계 E: 제3자 애플리케이션이 액세스 토큰을 사용하여 리소스 서버에 보호된 리소스 요청
단계 F: 리소스 서버가 액세스 토큰을 검증하고, 유효한 경우 요청을 처리
권한 코드와 액세스 토큰
권한 서버로부터 액세스 토큰을 얻기 위한 네 가지 유형의 권한 부여가 있습니다. 여기서는 가장 안전하고 일반적인 방법인 권한 코드 방법에 대해서만 설명하겠습니다.
단계 A: 제3자 애플리케이션이 사용자에게 GitHub와 같은 권한 부여 방법을 선택하도록 하고, client_id
및 redirect_uri
와 같은 매개변수를 포함하여 사용자를 권한 서버로 리디렉션
요청 예시:
GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.com
단계 B: 사용자가 로그인하고 권한 부여
단계 C: 권한 서버가 redirect_uri
에 따라 사용자를 제3자 애플리케이션의 백엔드로 리디렉션하며, 권한 코드 제공
응답 예시:
HTTP/1.1 302 Found
Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA
&state=xyz
단계 D: 제3자 애플리케이션이 권한 코드를 사용하여 권한 서버로부터 액세스 토큰 교환
요청 예시:
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
단계 E: 권한 서버가 검증하고 액세스 토큰 반환
권한 서버의 응답 예시:
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
"example_parameter":"example_value"
}
구체적인 예시
Bob은 Rabbit이라는 소프트웨어를 사용하여 Amazon 주문을 예쁘게 출력하고 싶어합니다.
리소스 소유자 -> Bob
클라이언트(제3자 애플리케이션) -> Rabbit 소프트웨어
권한 서버 -> Amazon의 권한 서버
리소스 서버 -> 주문 정보를 저장하는 Amazon의 데이터베이스
보호된 리소스 -> Amazon의 Bob의 주문 정보
권한 코드와 액세스 토큰을 별도로 얻어야 하는 이유는 무엇인가요?
권한 코드와 액세스 토큰을 별도로 얻는 것은 보안 조치를 보장하기 위함입니다.
OAuth2 프로토콜에서 권한 코드는 클라이언트가 액세스 토큰으로 교환할 임시 코드입니다. 이 코드는 권한 서버로부터 얻으며, 사용자(리소스 소유자)는 클라이언트가 요청하는 정보를 확인하고 요청을 승인하거나 거부할 수 있습니다.
사용자가 성공적으로 로그인하고 권한을 부여하면, 임시 권한 코드가 포함된 URL로 애플리케이션으로 리디렉션됩니다.
이 권한 코드는 일반적으로 10분 동안 유효하며, 한 번만 사용할 수 있습니다. 짧은 유효 기간은 사용자 정보가 유출될 위험을 줄입니다. 반면, access_token의 유효 기간은 상대적으로 길며, 일반적으로 1~2시간입니다. 이 토큰이 유출되면 사용자 데이터 보안에 더 큰 위험이 될 수 있습니다.
또한, 액세스 토큰을 교환하기 위해 클라이언트는 권한 코드 외에도 client ID
와 client secret
을 제공해야 합니다. 권한 서버는 이러한 매개변수를 사용하여 클라이언트를 인증하고 액세스 토큰을 요청하는 주체가 신뢰할 수 있는지 확인합니다. 권한 코드가 유출되더라도 해커가 client ID
와 client secret
을 가지고 있지 않다면 액세스 코드를 얻을 수 없습니다. 해커가 어떻게든 client ID
와 client secret
을 가지고 있다 하더라도, 권한 코드는 한 번만 사용할 수 있기 때문에 클라이언트 서버와 경쟁해야 합니다. 이 메커니즘은 잠재적인 공격의 난이도를 크게 높입니다. 권한 코드를 얻는 단계를 건너뛰고 액세스 토큰을 직접 반환하면, 공격자가 액세스 토큰을 사용하여 사용자 정보를 쉽게 훔칠 수 있습니다.
OIDC (OpenID Connect)
OAuth를 사용하여 권한을 부여하는 목적은 무엇인가요? 사용자에 대한 다양한 정보를 얻기 위함입니다. 왜 출력을 표준화하여 제3자 애플리케이션이 직접 사용할 수 있도록 하지 않을까요?
OIDC는 이러한 표준화를 수행합니다.
OIDC는 어떻게 이를 수행할까요? 간단히 말해, access token
과 함께 기본 사용자 정보를 포함하는 JWT
형식의 id_token
을 추가로 반환합니다. 제3자 애플리케이션은 서명 알고리즘을 확인하고 공개 키로 id_token
의 서명을 검증하여 사용자 정보를 얻을 수 있습니다.
또한, OIDC는 UserInfo 엔드포인트를 제공합니다. 제3자 서비스는 액세스 토큰을 사용하여 이 엔드포인트에 접근하여 사용자에 대한 추가 정보를 얻을 수 있습니다.
OIDC의 또 다른 기능은 Single Sign On (SSO) 및 Single Log Out (SLO)입니다. SSO는 사용자가 매번 자격 증명을 제공하지 않고도 다른 클라이언트와 인증된 세션을 가질 수 있도록 하여 사용성을 향상시킵니다. 사용자가 한 애플리케이션에 성공적으로 로그인하면 다른 애플리케이션에 로그인할 때 비밀번호를 다시 입력할 필요가 없습니다.
SLO는 사용자가 단일 로그아웃을 시작한 후 SSO 세션에서 남은 활성 세션이 없도록 하여 보안을 강화합니다. 사용자는 한 번만 로그아웃하면 모든 세션이 종료되어 세션이 하이재킹되거나 오용되는 것을 방지합니다.
OIDC는 비밀번호나 얼굴 인식과 같은 특정 인증 방법을 표준화하는 것이 아니라는 점에 유의해야 합니다. 대신, 중앙 집중식 인증 제공자에게 인증을 위임하는 방법, 인증 후 얻는 것 - id token
, 이 토큰이 어떻게 검증되는지 - JWT
형식, 그리고 이 id token
이 포함하는 사용자 정보를 지정합니다. 따라서 제3자 애플리케이션은 더 이상 바퀴를 재발명할 필요가 없습니다.
APISIX의 OAuth/OIDC 지원
Apache APISIX는 오픈 소스 클라우드 네이티브 API 게이트웨이입니다. 이는 동적, 실시간, 고성능 API 게이트웨이로, 전통적인 북-남 트래픽뿐만 아니라 서비스 간의 동-서 트래픽도 처리할 수 있습니다. 또한 k8s ingress 컨트롤러로 사용할 수 있습니다.
APISIX는 여러 업스트림 애플리케이션 서버를 위한 프록시 역할을 하는 API 게이트웨이이므로, 중앙 집중식 권한 부여 및 인증을 API 게이트웨이에 배치하는 것이 가장 자연스럽습니다.
APISIX의 OpenID Connect (OIDC) 플러그인은 OpenID Connect 프로토콜을 지원합니다. 사용자는 이 플러그인을 사용하여 APISIX를 Okta, Keycloak, Ory Hydra, Authing 등과 같은 많은 ID 제공자(IdP)와 연결하고 중앙 집중식 인증 게이트웨이로 배포할 수 있습니다. OIDC는 OAuth의 상위 집합이므로 이 플러그인은 OAuth도 지원합니다.
배포 다이어그램:
구성 예시: Apache APISIX와 Keycloak 통합하여 인증 구성
Keycloak 구성
매개변수 | 값 |
---|---|
keycloak 주소 | http://127.0.0.1:8080/ |
Realm | myrealm |
클라이언트 유형 | OpenID Connect |
클라이언트 ID | myclient |
클라이언트 시크릿 | e91CKZQwhxyDqpkP0YFUJBxiXJ0ikJhq |
리디렉션 URI | http://127.0.0.1:9080/anything/callback |
Discovery | http://127.0.0.1:8080/realms/myrealm/.well-known/openid-configuration |
로그아웃 URI | /anything/logout |
사용자 이름 | myuser |
비밀번호 | myrealm |
Realm | mypassword |
샘플 코드
curl -XPUT 127.0.0.1:9080/apisix/admin/routes/1 -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" -d '{
"uri":"/anything/*",
"plugins": {
"openid-connect": {
"client_id": "myclient",
"client_secret": "e91CKZQwhxyDqpkP0YFUJBxiXJ0ikJhq",
"discovery": "http://127.0.0.1:8080/realms/myrealm/.well-known/openid-configuration",
"scope": "openid profile",
"bearer_only": false,
"realm": "myrealm",
"redirect_uri": "http://127.0.0.1:9080/anything/callback",
"logout_path": "/anything/logout"
}
},
"upstream":{
"type":"roundrobin",
"nodes":{
"httpbin.org:80":1
}
}
}'
API가 성공적으로 생성된 후 http://127.0.0.1:9080/anything/test에 접속하면, 로그인하지 않았기 때문에 Keycloak의 로그인 페이지로 리디렉션됩니다:
사용자 이름으로 myuser, 비밀번호로 mypassword를 입력하면 다음 페이지로 이동합니다.
http://127.0.0.1:9080/anything/logout에 접속하여 로그아웃:
요약
요약하자면, OAuth 표준은 애플리케이션과 사용자 모두에게 인기 있는 솔루션입니다. 이는 사용자가 자격 증명을 공유하지 않고도 여러 플랫폼에서 서비스를 이용할 수 있도록 하여 편의성과 보안을 제공합니다. Apache APISIX는 다양한 ID 제공자(Keycloak, Ory Hydra, Okta, Auth0 등)와의 통합을 지원하여 API를 보호하는 인기 있는 API 게이트웨이입니다.
더 많은 세션 읽기: