학습 목표
인가(Authorization) 가 인증 이후 행동·리소스를 제한함을 설명할 수 있다.
RBAC(역할 기반)와 ABAC(속성 기반)의 구조·트레이드오프를 비교할 수 있다.
수평·수직 권한(IDOR, privilege escalation)을 인가 설계 관점에서 구분할 수 있다.
Policy engine(OPA 등)으로 중앙 정책을 분리하는 패턴을 설명할 수 있다.
API에서 fail closed·403 vs 401·리소스 소유 검증을 적용할 수 있다.
문제 상황
- JWT
role: adminclaim만 보고 삭제 API를 열었다- 토큰 위조·오발급 시 전면 장애 — 서버 측 정책 없음
- "로그인만 하면" 모든 주문 조회 API가 동작한다
GET /orders/12345— 남의 orderId만 바꿔도 200 (IDOR)
isAdminboolean 하나로 전 기능 통제- 감사·최소 권한 불가 — 퇴사자 admin 플래그 미회수
- 마이크로서비스마다 if문 권한 복붙
- 정책 변경 시 10곳 배포 — drift
- "영업 시간만 허용" 같은 조건을 RBAC role에 억지로 넣었다
- role 폭발 —
admin_weekday_morning…
- role 폭발 —
Part 2 인증·토큰(4~7편)으로 누구는 확인했다. 이제 무엇을 할 수 있는지 — OWASP Broken Access Control의 예방 층.
본 글의 시나리오·예제는 방어·설계 학습 목적이다. 타인 데이터에 대한 무단 접근은 불법이다.
1. 인증 다음 — 인가
| 인증 (Authentication) | 인가 (Authorization) | |
|---|---|---|
| 질문 | 누구인가 | 이 행동·리소스 허용? |
| 성공 | identity 확립 (sub, session) |
policy allow |
| 실패 HTTP | 401 Unauthorized | 403 Forbidden |
| 토큰 | JWT·OIDC 검증 | claims 만으로 결정하지 않음 |

요청 처리 순서 (권장):
1. TLS 종료
2. 인증 — 세션·JWT 유효성 (401)
3. 인가 — 역할·소유·정책 (403)
4. 비즈니스 로직
- 앞서 least privilege — 인가가 최소 권한을 코드로 강제
- OAuth scope는 위임 범위 — 앱 내부 RBAC와 별개 (6~7편)
2. RBAC (Role-Based Access Control)
RBAC — 사용자에게 역할(role) 을 부여하고, 역할에 권한(permission) 을 묶는다.

| 개념 | 예 |
|---|---|
| User | user-42 |
| Role | member, support, admin |
| Permission | orders:read, orders:refund, users:delete |
| Assignment | user-42 → member |
# code/rbac-roles.yaml (발췌)
roles:
member:
permissions:
- orders:read_own
- orders:create
support:
permissions:
- orders:read_any
- orders:refund
admin:
permissions:
- users:manage
- orders:*
| 패턴 | 설명 |
|---|---|
| Flat RBAC | role → permission 직접 |
| Hierarchical | admin ⊃ support ⊃ member |
| Separation of duty | 결제 승인 ≠ 결제 실행 role 분리 |
# 의사 코드 — permission check
if not user.has_permission("orders:refund"):
raise Forbidden()
RBAC 한계
- 속성 조건 — "본인 주문만", "같은 tenant만" → role만으로 어색
- role 폭발 — 기능·조건 조합마다 role 추가
- 리소스 단위 —
order/123소유는 RBAC 밖 — ownership check 필요
3. ABAC (Attribute-Based Access Control)
ABAC — 주체·리소스·행동·환경 속성으로 정책을 평가한다.

| 속성 종류 | 예 |
|---|---|
| Subject | user.id, user.department, user.clearance |
| Resource | order.owner_id, doc.classification |
| Action | read, delete, export |
| Environment | time, ip_country, mfa_verified |
# code/abac-policy.rego (OPA v1 — 발췌)
package orders.authz
import rego.v1
default allow := false
allow if {
input.action == "read"
input.resource.type == "order"
input.subject.user_id == input.resource.owner_id
}
allow if {
input.action == "read"
input.resource.type == "order"
"support" in input.subject.roles
input.subject.tenant_id == input.resource.tenant_id
}
전체 정책·opa eval용 입력 예: code/abac-policy.rego, code/abac-input.example.json
| RBAC | ABAC | |
|---|---|---|
| 모델 | role → permission | 속성 + 정책 |
| 표현력 | coarse | fine-grained |
| 복잡도 | 낮음 | 정책·테스트 필요 |
| 감사 | role 변경 추적 | 정책 버전·decision log |
- ReBAC(Relationship-Based) — "팀 멤버", "파일 공유" 그래프 — Google Zanzibar 계열 (개념만)
- 실무 혼합 — RBAC로 기본 역할, ABAC·ownership으로 리소스 제한
4. 리소스 소유·ACL
인증됨 ≠ 이 리소스 접근 가능 — OWASP Broken Access Control 핵심.

| 실패 패턴 | 예 | 방어 |
|---|---|---|
| IDOR | /orders/999 ID만 변경 |
owner_id == sub 검증 |
| 수직 상승 | member가 /admin 호출 |
role·permission 서버 검증 |
| 수평 이동 | A 사용자가 B 데이터 | tenant·owner 쿼리 조건 |
| Mass assignment | role: admin body 주입 |
allowlist 필드 |
order = db.get_order(order_id)
if order.owner_id != current_user.id and not current_user.can("orders:read_any"):
raise Forbidden()
- JWT
roleclaim 신뢰만 — DB·policy engine과 교차 검증 - 목록 API —
WHERE owner_id = :current_user(필터 누락 = 유출)
5. Policy engine
분산 if문 대신 정책을 데이터로 — OPA, Cedar, AWS IAM JSON, Keycloak authorization.

| 도구 | 특징 |
|---|---|
| OPA (Rego) | K8s admission, API sidecar, 범용 |
| Cedar (AWS) | 정형 정책 언어, 명시적 deny |
| IAM policy | cloud 리소스 ARN·action |
| 앱 내 RBAC 테이블 | 소규모 — DB roles_permissions |
API Gateway / Sidecar
→ POST /v1/data/orders/authz/allow
→ input: subject, action, resource
→ allow: true | false
- fail closed — engine 오류 시 deny (앞서 fail secure)
- 정책 단위 테스트 —
allow/denyfixture (CI) - decision log — 감사·디버그 (Observability 연계)
미들웨어 예: code/authz-middleware.example
6. 구현 패턴
| 패턴 | 설명 |
|---|---|
| Centralized | authz service / OPA — 단일 진실 |
| Embedded | 프레임워크 @Requires("orders:refund") |
| Data layer | ORM scope·row-level security (PostgreSQL RLS) |
| BFF | 프론트 메뉴 숨김 ≠ 인가 — API에서 강제 |
GET /api/orders/12345 HTTP/1.1
Authorization: Bearer eyJ...
| 응답 | 의미 |
|---|---|
| 401 | 토큰 없음·만료·서명 실패 |
| 403 | 인증됐으나 정책 거부 |
| 404 | 존재 숨김 (owner 아닌 리소스 — 정책 선택) |
- 403 vs 404 — 정보 노출 트레이드오프 — 팀 일관 규칙
- Admin API — 별도 prefix + 강한 MFA + IP allowlist (defense in depth)
7. 보안 체크리스트
| 항목 | 내용 |
|---|---|
| 순서 | 인증 → 인가 — 인가만으로 identity 추론 금지 |
| JWT claims | role 참고 — DB·policy 최종 |
| CRUD | create/read/update/delete permission 분리 |
| ownership | 모든 ID 기반 API owner 검증 |
| tenant | SaaS tenant_id 쿼리·정책 필수 |
| default | deny all — 명시 allow만 |
| test | IDOR·vertical 자동 테스트 (OWASP ZAP, unit) |
| audit | deny·admin action 로그 (15편) |
| 안티패턴 | |
|---|---|
| 프론트만 메뉴 숨김 | API 직접 호출 |
isAdmin boolean |
permission 세분화 없음 |
| role in JWT only | 철회·변경 지연 |
| 200 on forbidden | 403/404 명확 |
8. 정리
- 인가 — 인증 후 행동·리소스 허용 — 401과 403 구분
- RBAC — role·permission — 단순·운영에 강함
- ABAC — 속성·정책 — 조건·tenant·소유 표현
- ownership — RBAC 보완 — IDOR 방지 필수
- Policy engine — 정책 중앙화·테스트·감사
다음에 다룰 것
- Injection
- SQLi, command injection, 파라미터화
해당 내용은 NIST Role-Based Access Control, OWASP Authorization Cheat Sheet, Open Policy Agent Documentation 의 내용을 기반으로 합니다.