Network/Zero Trust

[Zero Trust - IAM] RBAC의 구조적 한계와 ABAC·ReBAC의 설계 차이

러러 2026. 3. 19. 23:03

Alice는 마케팅팀 소속 직원이다. 오늘 새벽 3시, 브라질 IP에서 접속했다. 지난주에 피싱 메일을 클릭한 이력이 있다. 이 사람에게 고객 데이터 CSV 다운로드를 허용해야 하는가?

 

대부분의 시스템은 이 질문에 대답하지 못한다. "Alice는 마케팅팀 역할을 갖고 있고, 마케팅팀은 고객 데이터에 접근할 수 있다"는 사실만 알 뿐이다. 새벽 3시라는 사실도, 브라질 IP라는 사실도, 피싱 클릭 이력도 접근 결정에 개입하지 않는다.

 

이것이 Role-Based Access Control(RBAC)의 한계이고, 이 한계를 메우려는 시도가 ABAC, ReBAC로 이어진 흐름이다. 각 모델은 "접근을 어떻게 결정할 것인가"라는 질문에 서로 다른 방식으로 대답한다.


RBAC — 역할이라는 단순한 약속

RBAC가 등장한 것은 1990년대 초다. 그 이전에는 각 사용자에게 직접 권한을 부여했다. 사용자가 100명이고 리소스가 50개라면 권한 매핑이 5,000개가 될 수 있었다. RBAC는 이 복잡성을 역할이라는 중간 계층으로 줄였다.

 

구조는 단순하다. 사용자는 역할에 할당되고, 역할은 권한을 갖는다. Alice가 marketing 역할을 갖고 있고, marketing 역할이 customer_data:read 권한을 갖고 있으면 — Alice는 고객 데이터를 읽을 수 있다.

RBAC가 암묵적으로 전제하는 것이 있다. 신원이 결정된 순간 권한이 결정된다. 접근 요청 시점의 맥락 (지금 몇 시인지, 어디서 접속했는지, 최근 계정에 이상 징후가 있었는지)은 이 모델의 바깥에 있다. 역할은 정적 매핑이다.

 

NIST는 RBAC를 네 단계로 정의했다.

  1. Flat RBAC(단순 역할 매핑)
  2. Hierarchical RBAC(역할 상속)
  3. Constrained RBAC(직무 분리)
  4. Symmetric RBAC(권한 검토)

실무에서 대부분의 시스템이 Flat에 머무는 이유는 단순히 구현이 쉬워서가 아니다. 상위 단계로 올라갈수록 운영 복잡성이 기하급수적으로 올라가기 때문이다.

 

Constrained RBAC가 대표적이다. 이 단계는 직무 분리(Separation of Duty, SoD)를 RBAC 안에서 강제하려는 시도다.

재무팀 직원이 동시에 지출 요청자와 승인자 역할을 가질 수 없어야 한다.

 

이것이 SoD의 핵심이다. 이를 정적으로 강제하는 Static SoD는 역할 할당 시점에 충돌을 막는다.

 

그런데 같은 직원이 프로젝트 A에서는 지출 요청자이고 프로젝트 B에서는 승인자여야 하는 경우가 생기면? 이런 맥락 의존적 제약은 Dynamic SoD로 처리해야 하고, Dynamic SoD는 세션 단위로 역할 충돌을 판단해야 하기 때문에 상태를 추적해야 한다. 상태를 추적하는 순간 RBAC는 정적 모델이 아니게 된다.

Role Explosion — 한계가 터지는 순간

RBAC가 무너지는 시점은 예측 가능하다. 컨텍스트를 반영해야 하는 요구사항이 생기는 순간이다.

"마케팅팀은 고객 데이터에 접근할 수 있다. 단, 오피스 시간에만." 이 요구사항을 RBAC로 표현하려면 역할을 쪼개야 한다.

 

marketingmarketing_office_hours. "EU 리전 사용자는 EU 데이터에만 접근할 수 있다"는 요구사항이 더해지면 marketing_office_hours_eu가 생긴다. 여기에 팀장 승인 여부, 디바이스 유형, 계약직 여부가 더해지면 역할 이름이 길어질 뿐 아니라 역할의 수 자체가 폭발한다.

 

이것을 Role Explosion이라고 부른다. 역할 증식은 운영 문제처럼 보이지만, 실제로는 모델이 컨텍스트를 표현할 수 없기 때문에 역할로 우회하는 구조적 증상이다. 역할을 아무리 늘려도 Alice의 지금 이 순간 상황을 표현할 수는 없다.


ABAC — 질문을 바꾼 모델

Attribute-Based Access Control(ABAC)는 RBAC를 개선한 것이 아니다. 접근 제어를 다른 함수로 표현한 것이다.

RBAC의 접근 결정 함수는 이렇게 생겼다.

permit(user, resource) = role(user) ∈ acl(resource)

사용자의 역할이 리소스의 접근 목록에 있으면 허용한다. 입력이 두 개(사용자, 리소스)이고, 역할이라는 중간 레이어가 있다.

ABAC의 함수는 다르다.

permit(subject, resource, action, environment) =
  evaluate(
    policy, 
    attrs(subject), 
    attrs(resource), 
    action, 
    attrs(environment)
  )

 

입력이 네 개다. Subject(요청자), Resource(대상 리소스), Action(수행할 행위), Environment(접근 시점의 환경).

그리고 이 네 가지의 속성(attribute)을 조합해 정책을 평가한다.

Alice 시나리오를 이 함수로 표현하면 이렇게 된다.

subject.department == "marketing"
AND subject.risk_score < 0.3
AND environment.time BETWEEN 09:00 AND 18:00
AND environment.ip_region != "BR"

 

역할 대신 조건이다. RBAC에서라면 marketing_office_hours_non_highrisk_domestic 같은 역할이 필요했을 상황을 단일 정책으로 처리한다.

속성은 어디서 오는가

ABAC에서 자주 지나치는 질문이 있다. 속성은 어디서 오는가. subject.risk_score는 누가 계산하고, 어디에 저장하며, 접근 요청 시점에 어떻게 조회되는가.

 

실제 구현에서 속성 소스는 다양하다.

  • 사용자 속성(부서, 직급, 고용 형태)은 Identity Provider(IdP)에서 온다.
  • 기기 속성(OS 버전, 패치 상태, MDM 등록 여부)은 엔드포인트 관리 시스템에서 온다.
  • 리스크 점수는 UEBA(User and Entity Behavior Analytics)나 별도 리스크 엔진이 실시간으로 산출한다.
  • 리소스 속성(데이터 분류 등급, 소유팀)은 CMDB나 데이터 카탈로그에 있다.

이 모든 소스를 접근 결정 시점에 조회하면 지연 시간이 생긴다. 미리 캐싱하면 최신성 문제가 생긴다.

ABAC 아키텍처가 실제로 복잡한 이유는 정책 언어가 아니라 속성 파이프라인 때문이다. 

 

이 복잡성은 자연스럽게 또 다른 질문을 낳는다. 서비스마다 흩어진 접근 제어 로직을 어떻게 한 곳에서 관리할 것인가.

 OASIS의 eXtensible Access Control Markup Language(XACML, 2003)가 이 방향을 처음 제도화 했다.

XACML은

  • Policy Decision Point(PDP): 정책을 평가해서 “허용/거부” 같은 최종 결정을 내리는 핵심 엔진
  • Policy Enforcement Point(PEP): 요청을 가로채고 PDP의 결정 결과를 실제로 적용(허용/차단)하는 지점
  • Policy Administration Point(PAP): 정책을 정의·관리·배포하는 관리자 영역 (룰 작성하는 곳)
  • Policy Information Point(PIP): 정책 평가에 필요한 사용자, 리소스, 환경 정보 등을 제공하는 데이터 소스

의 역할을 명확히 분리했다. 오늘날 OPA나 Cedar 같은 현대적 정책 엔진들은 XACML의 아키텍처적 통찰을 이어받되 XML의 복잡성을 걷어낸 형태다.

ABAC의 한계 — 관계를 속성으로 평탄화하는 문제

ABAC는 컨텍스트 표현에 강하지만 한 가지를 잘 못한다. 객체 간 관계(relationship)를 표현하는 것.

"Alice가 공유한 문서를 Bob이 볼 수 있는가?" 이 질문을 ABAC로 표현하려면 관계를 속성으로 평탄화해야 한다.

 

resource.shared_with 속성에 Bob이 포함돼 있는지 확인하는 방식이다. 그런데 Alice가 폴더를 Bob과 공유했고, 그 폴더 안에 문서가 있다면? 폴더 공유 관계가 하위 문서에 전이되는 것을 속성으로 표현하려면 어딘가에서 그 관계를 미리 계산해 속성으로 주입해야 한다.

 

관계가 깊어질수록, 조직 구조가 복잡해질수록 이 평탄화는 한계에 부딪힌다. 더 근본적으로는, ABAC가 속성을 입력으로 받는 함수인 이상 "관계"는 항상 함수 외부에서 계산된 결과를 주입하는 방식으로 처리된다. 관계 자체가 모델의 1등 시민이 아니다.


ReBAC — 관계 모델

Relationship-Based Access Control(ReBAC)는 ABAC와 PBAC가 외부에서 주입받아야 했던 것을 모델 안으로 가져온다. 관계(relationship) 자체를 접근 제어의 1등 시민으로 다룬다.

 

ReBAC의 핵심 아이디어는 접근 제어 질문을 그래프 탐색 문제로 재정의하는 것이다.

"Alice가 doc:123을 편집할 수 있는가?"

 

이 질문을 ReBAC는 이렇게 다시 쓴다. user:alice에서 doc:123으로 이어지는 editor 관계 경로가 존재하는가?

 

관계는 Relation Tuple이라는 단위로 표현된다.

(doc:123, editor, user:alice)
  • 객체(object)
  • 관계(relation)
  • 주체(subject)

위 세 값으로 표현된다.

 

"doc:123의 editor는 user:alice다." 이 tuple들이 모이면 관계 그래프가 된다. 접근 결정은 이 그래프에서 경로를 탐색하는 것이다.

상속과 전이 

관계 그래프의 진짜 힘은 상속에 있다. 다음 세 개의 tuple을 보자.

(doc:123, parent, folder:work)
(folder:work, viewer, user:bob)
(folder:work, viewer, user:carol)

 

첫 번째 tuple을 읽으면 이렇다. "doc:123의 parent는 folder:work다." 즉 doc:123이 folder:work 안에 있다는 관계를 표현한다.

 

Bob과 Carol은 folder:work의 viewer다. "Bob이 doc:123을 볼 수 있는가?"를 판단하려면 folder:work의 viewer는 하위 문서의 viewer이기도 하다는 규칙이 필요하다.

 

ReBAC에서는 이 규칙을 명시적으로 선언한다.

doc#viewer = folder#viewer (folder가 doc의 parent인 경우)

이제 새 문서를 folder:work 안에 추가할 때마다 Bob과 Carol의 접근 권한을 따로 부여할 필요가 없다. 폴더의 viewer 관계가 자동으로 전이된다. 조직 구조나 폴더 계층이 바뀌어도 관계 그래프만 업데이트하면 된다.

 

이 모델이 Google Drive, Gmail, YouTube 같은 서비스에 단일한 권한 시스템으로 적용된 것이 Zanzibar다. 수십억 개의 객체, 수천억 개의 관계 tuple을 처리하면서 일관성을 보장한다.

ReBAC의 한계

관계 그래프를 별도로 유지해야 한다는 것은 단순히 스토리지 하나를 더 쓰는 문제가 아니다. 관계 데이터는 접근 요청 경로에 직접 있기 때문에 조회 지연이 허용 범위 안에 있어야 한다. 그래프 깊이가 깊어질수록 "사용자 → 팀 → 조직 → 프로젝트 → 폴더 → 문서" 탐색해야 하는 경로가 길어지고, 이것이 P99 지연 시간에 직접 영향을 준다. Zanzibar가 범위 제한(depth bound)과 공격적 캐싱을 설계의 핵심 요소로 삼은 것은 이 이유 때문이다.

 

순환 관계도 다뤄야 할 문제다. 그룹 A가 그룹 B를 포함하고 그룹 B가 다시 그룹 A를 포함하는 순환이 생기면, 관계 평가 엔진은 무한 루프에 빠질 수 있다. ReBAC 구현체들은 탐색 깊이 제한 또는 방문 노드 추적으로 이를 방어한다. 모델링 실수가 조용한 권한 누락이나 무한 탐색으로 이어질 수 있다는 뜻이기도 하다.

 

수치 조건(리스크 점수, 시간대)은 여전히 ABAC와 조합해서 처리해야 한다. 순수 ReBAC로는 "리스크 점수가 0.3 미만인 경우에만" 같은 조건을 표현하기 어렵다. 실제 시스템에서 ReBAC는 관계 기반 경로 탐색을 담당하고, 환경 조건 검증은 PDP 레이어가 보완하는 방식으로 조합된다.


세 모델 비교

  RBAC ABAC ReBAC
접근 결정 기준 역할 매핑 속성 조합 함수 관계 그래프 탐색
컨텍스트 반영 불가 가능 관계 중심
표현하기 어려운 것 환경·리스크 조건 깊은 객체 관계 수치 임계값 조건
정책 위치 코드·DB 코드·DB 관계 스토리지
감사 용이성 높음 중간 중간 (그래프 추적)
대표 구현 Casbin, AWS IAM Roles OPA(ABAC 모드) SpiceDB, OpenFGA

모델 판단 기준

어느 모델이 더 좋은가는 틀린 질문이다. 어떤 접근 제어 질문을 주로 받는 시스템인가에 따라 달라진다. 실제 제품들이 어떤 선택을 했는지 보면 판단 기준이 더 선명해진다.

  • GitHub
    조직(org) → 팀(team) → 레포지토리(repo) 계층에서 권한이 전이되는 구조를 갖는다. 팀에 부여된 write 권한이 팀 멤버 전체에 전이되고, fork 관계나 외부 collaborator 추가가 권한 전이에 영향을 준다. 이 구조는 RBAC로 표현하면 Role Explosion으로 이어진다. GitHub의 권한 모델이 ReBAC에 가까운 방향으로 발전한 것은 이 이유에서다.
  • AWS IAM
    RBAC(역할)와 ABAC(태그 기반 조건)를 명시적으로 조합한다. IAM 역할로 큰 그림의 권한을 잡고, Condition 블록에서 aws:ResourceTag, aws:RequestedRegion, aws:MultiFactorAuthPresent 같은 속성으로 세밀한 제어를 추가한다. 정적 권한 구조에 동적 조건을 얹는 전형적인 RBAC+ABAC 패턴이다.
  • Google Workspace
    Drive의 파일·폴더 공유, Gmail의 위임, Google Docs의 편집자·뷰어 관계 이 모든 것이 Zanzibar의 Relation Tuple로 표현된다. 수십억 사용자의 수천억 관계를 실시간으로 평가하는 시스템이 Zanzibar — ReBAC다. 

판단 기준을 정리하면 이렇다.

 

역할 집합이 안정적이고 10~20개 이내이며, 환경 변수가 접근 결정에 개입하지 않는다면 RBAC로 충분하다.

접근 결정에 현재 상태가 개입해야 한다면 — 로그인 리스크, 접속 위치, MFA 완료 여부 — ABAC이다.

 

여러 서비스에 걸쳐 일관된 정책이 필요하고 변경이 잦다면 OPA 같은 정책 엔진으로 외부화할 수도 있다. 또한 데이터 객체 간 소유·공유·계층 관계가 접근 제어의 핵심이라면 ReBAC다.

 

현실적으로는 단일 모델을 선택하기보다 조합하는 경우가 많다. RBAC로 큰 그림을 잡고, ABAC로 환경 조건을 추가하고, 특정 리소스 유형에만 ReBAC를 적용하는 식이다. 중요한 것은 각 모델이 어떤 질문에 답하는지 이해하고, 필요한 질문에 맞는 도구를 선택하는 것이다.


 

세 가지 모델 중 Google은 ReBAC를 선택했다. 그것도 Gmail, Drive, YouTube, Google Maps 전체에 단일 시스템으로 적용했다. 수십억 개의 객체, 수천억 개의 관계 tuple, 전 세계 분산 환경에서 일관성 보장. 이 문제를 Google이 어떻게 풀었는지를 담은 논문이 Zanzibar다.

 

다음에는 Zanzibar 논문을 해부한다. Relation Tuple의 구조, userset rewrite rules로 관계 상속을 선언하는 방법, 그리고 분산 환경에서 일관성을 보장하기 위해 도입한 zookie의 설계를 다룬다.