읽기용 강의가 아니라 실무에서 참고하는 레퍼런스다. 평가 모델 → 연산자 표 → 실무에서 무는 함정 → 이 repo의 검증된 정책 → 직접 손으로 짜보기. 1차 출처는 공식 Cedar 문서.
default-deny. 매칭되는 permit이 하나 이상이고 forbid가 0개면 Allow. 그 외 전부 Deny.
forbid는 어떤 permit도 이긴다. permit 여러 개는 OR로 결합되지만, forbid 하나가 매칭되면 끝(Deny).
정책 = effect(permit|forbid) + scope(principal, action, resource — 필수) + 선택 when/unless. ;로 끝.
scope에는 == · in · is · 무제약만. 나머지 비교·로직은 전부 when/unless 안에서.
| 범주 | 연산자 | 의미 / 주의 |
|---|---|---|
| scope 전용 | == in is | in=엔티티 계층(그룹) 멤버십, is=타입. scope엔 이 셋(+무제약)만. |
| 비교 | == != < <= > >= | 대소비교는 long/datetime/duration. !=는 when/unless에서만(scope 불가). decimal은 .lessThan() 등. |
| 로직 | && || ! if…then…else | 단락 평가(short-circuit) — 왼쪽이 결정나면 오른쪽 평가 안 함. 에러 가드에 활용. |
| 계층·타입 | in is is T in P | in은 전이적·반사적: A in A는 A가 store에 없어도 true. |
| 속성·문자열 | .attr has like | has=속성 존재 확인(missing 에러 방지). like "a*c" 와일드카드(\*=리터럴). |
| 집합 | .contains() .containsAll() .containsAny() .isEmpty() | set 멤버십/포함/공집합. |
① 에러나는 정책은 *조용히 스킵*된다 → forbid가 에러나면 fail-OPEN
없는 속성 접근·타입 불일치는 그 정책을 ERROR로 만들고, Cedar는 그 정책을 무시한다. 막아야 할 forbid가 에러나면, 막혀야 할 게 통과한다.
가드: principal has limit && principal.limit > 0 처럼 has로 먼저 막아라(단락 평가).
// 실화: 이 repo의 에이전트 위임 정책에서 dangling 엔티티가 P6 forbid를 ERROR로 만들어 권한이 증폭됐다. 자체 적대 감사가 잡아 entity→scalar로 고침. THREAT_MODEL / cedar/agent 참고.
② 경계값 — "한도 이하"는 <=
<로 쓰면 정확히 한도(1000)인 이체가 거부된다. off-by-one은 인가에서 사고로 직결.
③ 엔티티 ID 재사용은 권한 누수다
떠난 User::"jane"의 이름을 새 사용자에게 재발급하면, 옛 정책이 참조하던 권한이 자동 승계된다. 실무에선 UUID를 쓰고 // jane 주석으로 가독성만 보완.
④ validator는 evaluator보다 엄격하다
평가(런타임)는 통과해도 schema validate에서 막힐 수 있다. 둘 다 돌려라 — 이 repo는 cedar/authz.py가 schema validate + 8 시나리오를 함께 본다.
위 모델·함정을 손에 익힌다. labs/m0/policies.cedar를 채우고 python labs/m0/grade.py로 채점.
가장 단순한 permit. scope + 한 줄 when.
permit (
principal,
action == Action::"ViewAccount",
resource
)
when { resource.owner == principal };
principal·resource는 무제약(누구/무엇이든) → 실제 제약은 when에서.action == Action::"ViewAccount": 이 액션에만 적용. 다른 액션 요청엔 이 정책이 안 걸린다(=이 정책으론 Allow 안 줌).resource.owner == principal: 호출자가 그 리소스의 owner일 때만. 양쪽이 같은 엔티티 타입(User)이어야 한다.함정 ①(에러 가드)과 ②(경계값)가 둘 다 걸린다. 연산자를 채워라.
permit (
principal,
action == Action::"Transfer",
resource
)
when {
resource.owner == principal
&& context.amount > 0
&& context.amount __?__ principal.transferLimit
};
힌트 — 정확히 한도만큼(1000) 이체도 허용해야 한다(함정 ②). 그리고 context.amount > 0를 앞에 둔 건 단락 평가로 음수/0을 먼저 쳐내려는 것.
&& context.amount <= principal.transferLimit
<가 아니라 <=. principal에 transferLimit이 항상 있다고 가정 중인데, 없을 수 있다면 principal has transferLimit && …로 가드(함정 ①).
ViewAuditLog는 Role::"auditor"만. 계층 멤버십은 principal in Role::"auditor" — when 조건 불필요.forbid의 첫 등장. forbid는 permit을 이긴다(멘탈 모델). 이체를 절대 막을 조건 → resource.frozen == true. frozen이 optional이면 resource has frozen 가드.ViewAccount만.python labs/m0/grade.py # R1–R4 → 8/8
python labs/m0/grade.py --ext # E1 포함 → 11/11
python labs/m0/grade.py --hint # FAIL 행마다 요건 넛지
// 사실은 공식 Cedar 문서 + 이 repo의 검증된 정책에 근거. "실무 숙련"은 이 레퍼런스 + 직접 reps(grade.py)에서 나온다 — 읽기만으로 되지 않는다.