<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Permission Drift on jyukki's Blog</title><link>https://jyukki.com/tags/permission-drift/</link><description>Recent content in Permission Drift on jyukki's Blog</description><generator>Hugo -- 0.147.0</generator><language>ko-kr</language><lastBuildDate>Wed, 08 Jul 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://jyukki.com/tags/permission-drift/index.xml" rel="self" type="application/rss+xml"/><item><title>백엔드 커리큘럼 심화: Permission Drift와 Access Review, 오래된 권한을 자동으로 회수하는 운영 설계</title><link>https://jyukki.com/learning/deep-dive/deep-dive-permission-drift-access-review-playbook/</link><pubDate>Wed, 08 Jul 2026 00:00:00 +0000</pubDate><guid>https://jyukki.com/learning/deep-dive/deep-dive-permission-drift-access-review-playbook/</guid><description>RBAC·ABAC·ReBAC로 권한을 설계한 뒤 시간이 지나며 생기는 권한 드리프트를 탐지하고, 접근권 검토·자동 회수·감사 증거로 운영하는 기준을 정리합니다.</description><content:encoded><![CDATA[<p>권한 시스템은 처음 만들 때보다 운영하면서 더 어려워집니다. 신규 입사자에게 역할을 주고, 고객사 관리자에게 프로젝트 권한을 부여하고, 장애 대응을 위해 임시 admin 권한을 열어 주는 일은 비교적 쉽습니다. 문제는 시간이 지난 뒤입니다. 팀 이동, 프로젝트 종료, 외주 계약 만료, 임시 예외, 테스트 계정, 긴급 대응 계정이 쌓이면 실제 필요한 권한과 시스템에 남아 있는 권한이 어긋납니다. 이것이 **Permission Drift(권한 드리프트)**입니다.</p>
<p>권한 드리프트는 바로 장애로 보이지 않습니다. 사용자는 여전히 로그인되고, API도 정상 응답합니다. 하지만 사고가 났을 때 &ldquo;왜 이 사람이 아직 이 데이터에 접근할 수 있었나&rdquo;, &ldquo;왜 만료된 파트너 키가 여전히 정산 API를 호출했나&rdquo;, &ldquo;왜 임시 운영 권한이 6개월 동안 살아 있었나&rdquo; 같은 질문으로 돌아옵니다. 그래서 권한 운영은 <a href="/learning/deep-dive/deep-dive-authorization-models-rbac-abac-rebac/">인가 모델 실전 설계</a>에서 끝나지 않습니다. 권한을 계속 검토하고, 설명하고, 회수하는 <strong>Access Review 파이프라인</strong>이 필요합니다.</p>
<p>이 글은 <a href="/learning/deep-dive/deep-dive-authorization-decision-cache-invalidation-playbook/">권한 판정 캐시 무효화</a>, <a href="/learning/deep-dive/deep-dive-api-key-lifecycle-rotation-revocation-playbook/">API Key Lifecycle</a>, <a href="/learning/deep-dive/deep-dive-tamper-evident-audit-log-playbook/">Tamper-Evident Audit Log</a>, <a href="/learning/deep-dive/deep-dive-secret-management/">시크릿 관리</a>와 이어서 보면 좋습니다. 핵심은 권한을 &ldquo;부여 가능한 기능&quot;이 아니라 &ldquo;수명이 있는 운영 자산&quot;으로 다루는 것입니다.</p>
<h2 id="이-글에서-얻는-것">이 글에서 얻는 것</h2>
<ul>
<li>권한 드리프트가 어떤 경로로 생기고, 왜 초기 인가 설계만으로는 막기 어려운지 이해할 수 있습니다.</li>
<li>권한 grant row에 어떤 메타데이터를 남겨야 나중에 owner, reason, 만료, 사용 이력을 설명할 수 있는지 기준을 잡을 수 있습니다.</li>
<li>Access Review를 분기별 수동 점검이 아니라 위험도 기반 자동 후보 생성, owner 승인, 회수, 감사 로그로 연결하는 방법을 가져갈 수 있습니다.</li>
<li>고위험 권한, 임시 예외, 미사용 권한, API Key 권한을 어떤 숫자 기준으로 회수할지 정할 수 있습니다.</li>
</ul>
<h2 id="핵심-개념이슈">핵심 개념/이슈</h2>
<h3 id="1-권한-드리프트는-잘못-준-권한보다-회수하지-못한-권한에서-많이-생긴다">1) 권한 드리프트는 &ldquo;잘못 준 권한&quot;보다 &ldquo;회수하지 못한 권한&quot;에서 많이 생긴다</h3>
<p>권한 사고를 떠올리면 보통 처음부터 과도한 권한을 준 장면을 생각합니다. 물론 그것도 문제입니다. 하지만 운영에서는 처음에는 타당했던 권한이 시간이 지나면서 위험해지는 경우가 더 많습니다.</p>
<p>대표 패턴은 아래와 같습니다.</p>
<table>
  <thead>
      <tr>
          <th>패턴</th>
          <th>예시</th>
          <th>위험</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>조직 이동</td>
          <td>결제팀에서 마케팅팀으로 이동했지만 정산 조회 권한 유지</td>
          <td>업무상 불필요한 민감 데이터 접근</td>
      </tr>
      <tr>
          <td>임시 예외</td>
          <td>장애 대응용 admin 권한을 24시간만 쓰기로 했지만 만료 없음</td>
          <td>상시 superuser화</td>
      </tr>
      <tr>
          <td>프로젝트 종료</td>
          <td>외주 개발자 계정이 프로젝트 종료 뒤에도 repo/API 접근 가능</td>
          <td>계약 범위 밖 접근</td>
      </tr>
      <tr>
          <td>shadow group</td>
          <td>과거 권한 그룹이 더 이상 쓰이지 않지만 정책에 남음</td>
          <td>리뷰 누락과 역할 폭발</td>
      </tr>
      <tr>
          <td>미사용 키</td>
          <td>120일 미사용 API Key가 여전히 활성</td>
          <td>유출 시 탐지 지연</td>
      </tr>
  </tbody>
</table>
<p>이 문제는 RBAC, ABAC, ReBAC 중 무엇을 쓰느냐와 별개로 발생합니다. RBAC에서는 역할이 늘어나며 고아 역할이 생기고, ABAC에서는 예외 조건이 누적되고, ReBAC에서는 관계 edge가 끊기지 않습니다. 그래서 모델 선택 다음에는 반드시 권한 수명주기와 회수 경로를 설계해야 합니다.</p>
<h3 id="2-grant-row는-권한의-현재-상태가-아니라-부여-근거를-담아야-한다">2) grant row는 권한의 &ldquo;현재 상태&quot;가 아니라 &ldquo;부여 근거&quot;를 담아야 한다</h3>
<p>권한 테이블을 단순히 <code>user_id</code>, <code>role</code>, <code>created_at</code>으로 만들면 나중에 검토할 정보가 부족합니다. 왜 줬는지, 누가 승인했는지, 언제까지 필요한지, 최근 사용됐는지 알 수 없기 때문입니다. Access Review를 운영하려면 권한 grant 자체에 검토 가능한 필드가 있어야 합니다.</p>
<p>권장 필드는 아래 정도입니다.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>permission_grants
</span></span><span style="display:flex;"><span>- grant_id
</span></span><span style="display:flex;"><span>- subject_type        # user, service_account, api_key, group
</span></span><span style="display:flex;"><span>- subject_id
</span></span><span style="display:flex;"><span>- resource_type
</span></span><span style="display:flex;"><span>- resource_id
</span></span><span style="display:flex;"><span>- permission          # orders:read, billing:admin, project:write
</span></span><span style="display:flex;"><span>- risk_tier           # low, medium, high, critical
</span></span><span style="display:flex;"><span>- source_system       # HR, IAM, admin_console, break_glass, migration
</span></span><span style="display:flex;"><span>- granted_by
</span></span><span style="display:flex;"><span>- approved_by
</span></span><span style="display:flex;"><span>- reason_code
</span></span><span style="display:flex;"><span>- ticket_id
</span></span><span style="display:flex;"><span>- created_at
</span></span><span style="display:flex;"><span>- expires_at
</span></span><span style="display:flex;"><span>- last_used_at
</span></span><span style="display:flex;"><span>- last_reviewed_at
</span></span><span style="display:flex;"><span>- review_owner
</span></span><span style="display:flex;"><span>- status              # active, expiring, suspended, revoked
</span></span></code></pre></div><p>여기서 <code>reason_code</code>, <code>ticket_id</code>, <code>expires_at</code>이 특히 중요합니다. 이유와 만료일이 없는 권한은 시간이 지나면 설명할 수 없습니다. 운영 기준으로는 high-risk 권한의 <code>expires_at</code> 누락률을 **0%**로 두는 편이 안전합니다. medium 이하 권한도 owner와 reason은 반드시 가져야 합니다.</p>
<h3 id="3-access-review는-전체-권한-목록을-뿌리는-일이-아니다">3) Access Review는 전체 권한 목록을 뿌리는 일이 아니다</h3>
<p>많은 팀이 분기마다 권한 목록을 CSV로 뽑아 팀장에게 보내고 &ldquo;확인해 주세요&quot;라고 합니다. 이 방식은 오래 못 갑니다. 검토자는 수백 행을 보고 의미를 파악하지 못하고, 결국 모두 승인하거나 아무것도 회수하지 않습니다. 좋은 Access Review는 사람에게 전체 데이터를 던지지 않고, 시스템이 먼저 위험 후보를 줄여 줍니다.</p>
<p>후보 생성 기준은 최소 네 가지입니다.</p>
<ol>
<li><strong>위험도</strong>
<ul>
<li>admin, export, billing, production write, customer PII read는 우선 검토</li>
</ul>
</li>
<li><strong>사용 이력</strong>
<ul>
<li>30~90일 미사용 권한은 회수 후보</li>
</ul>
</li>
<li><strong>조직/관계 변화</strong>
<ul>
<li>부서 이동, 그룹 탈퇴, 프로젝트 종료, 계약 만료 이벤트와 비교</li>
</ul>
</li>
<li><strong>예외성</strong>
<ul>
<li>break-glass, manual grant, 정책 우회, 만료 연장 이력이 있는 권한</li>
</ul>
</li>
</ol>
<p>즉 &ldquo;모든 권한을 검토하라&quot;보다 &ldquo;이 37개 권한은 회수 후보이고, 이 중 9개는 7일 안에 결정해야 한다&quot;로 만들어야 합니다. 사람은 도메인 판단을 하고, 시스템은 후보를 압축해야 합니다.</p>
<h3 id="4-권한-회수는-권한-부여보다-더-위험할-수-있다">4) 권한 회수는 권한 부여보다 더 위험할 수 있다</h3>
<p>보안 관점에서는 안 쓰는 권한을 빨리 지우고 싶습니다. 하지만 운영에서는 회수 자체가 장애를 만들 수 있습니다. 오래된 배치 job이 어떤 API Key로 돌아가는지 모르거나, 파트너사가 아직 이전 scope를 쓰고 있거나, 고객 지원팀의 긴급 조회 권한을 갑자기 빼면 업무가 멈출 수 있습니다.</p>
<p>그래서 회수는 단계가 필요합니다.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>detect -&gt; notify -&gt; grace period -&gt; suspend -&gt; revoke -&gt; monitor
</span></span></code></pre></div><ul>
<li><code>detect</code>: 미사용, 조직 변경, 만료 초과 후보 탐지</li>
<li><code>notify</code>: subject와 owner에게 회수 예정 알림</li>
<li><code>grace period</code>: low/medium은 7<del>14일, high-risk는 1</del>3일 또는 즉시</li>
<li><code>suspend</code>: 필요하면 soft block으로 먼저 막고 복구 가능하게 둠</li>
<li><code>revoke</code>: 최종 회수, 복구는 새 승인으로만 허용</li>
<li><code>monitor</code>: 회수 뒤 403, job failure, support ticket 증가 확인</li>
</ul>
<p>의사결정 기준은 <strong>critical 권한은 안전 우선, low 권한은 운영 안정성 우선</strong>입니다. 고객 데이터 export, 결제 취소, 권한 부여, production write 권한은 만료가 지났다면 즉시 중지에 가깝게 운영합니다. 반대로 읽기 전용 내부 대시보드 권한은 알림과 유예 기간을 두는 편이 현실적입니다.</p>
<h3 id="5-회수-후-권한-캐시와-세션을-같이-정리해야-한다">5) 회수 후 권한 캐시와 세션을 같이 정리해야 한다</h3>
<p>권한 row를 <code>revoked</code>로 바꿔도 애플리케이션이 캐시한 결정을 계속 쓰면 효과가 없습니다. 특히 JWT claim, 세션 캐시, API gateway cache, PDP local cache가 있으면 stale allow가 남을 수 있습니다. 권한 회수는 <a href="/learning/deep-dive/deep-dive-authorization-decision-cache-invalidation-playbook/">권한 판정 캐시 무효화</a>와 함께 설계해야 합니다.</p>
<p>권장 기준:</p>
<ul>
<li>critical 권한 회수: 1분 이내 전 경로 반영</li>
<li>high 권한 회수: 5분 이내 반영</li>
<li>일반 권한 회수: 캐시 TTL 15~60분 내 반영 가능</li>
<li>관리자 권한 회수: 활성 세션 재검증 또는 강제 logout</li>
<li>API Key revoke: 다음 요청부터 즉시 차단, revoked key 재사용 알림</li>
</ul>
<p>권한 캐시 TTL이 길다면 회수 이벤트를 push해야 합니다. 반대로 이벤트 파이프라인이 불안정하다면 TTL을 짧게 잡아야 합니다. &ldquo;언젠가 반영된다&quot;는 권한 회수에서는 부족합니다.</p>
<h2 id="실무-적용">실무 적용</h2>
<h3 id="1-4단계-도입-플랜">1) 4단계 도입 플랜</h3>
<p><strong>1단계: 권한 인벤토리</strong></p>
<p>먼저 권한을 부여하는 경로를 모두 찾습니다. 관리자 UI, IAM sync, DB script, migration, support tool, API Key 발급, break-glass 절차가 후보입니다. 권한 row가 한 테이블에 없더라도 상관없습니다. 중요한 것은 &ldquo;어디서 권한이 생기고 사라지는가&quot;를 그리는 것입니다.</p>
<p>출발 기준:</p>
<ul>
<li>high-risk permission 목록 20개 이내로 정의</li>
<li>권한 부여 경로 100% 식별</li>
<li>owner 없는 권한 grant 비율 측정</li>
<li>90일 이상 미사용 grant 수 계산</li>
</ul>
<p><strong>2단계: grant metadata 보강</strong></p>
<p>기존 권한 테이블에 필드를 한 번에 다 넣기 어렵다면 shadow table로 시작해도 됩니다. <code>grant_id</code>, <code>owner</code>, <code>reason</code>, <code>expires_at</code>, <code>last_used_at</code>, <code>risk_tier</code>만 있어도 리뷰 품질이 크게 올라갑니다.</p>
<p>최소 기준:</p>
<ul>
<li>신규 high-risk grant는 <code>expires_at</code> 필수</li>
<li>break-glass grant 기본 만료 24시간</li>
<li>외주/파트너 계정은 계약 종료일과 연결</li>
<li>service account/API Key는 owner 팀과 rotation owner 필수</li>
</ul>
<p><strong>3단계: 리뷰 후보 자동 생성</strong></p>
<p>매일 또는 매주 배치로 회수 후보를 만듭니다.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#ff79c6">review_candidate_rules</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">critical_expired</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">condition</span>: <span style="color:#f1fa8c">&#34;risk_tier in [critical, high] and expires_at &lt; now&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">action</span>: <span style="color:#f1fa8c">&#34;suspend_or_revoke&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">sla</span>: <span style="color:#f1fa8c">&#34;24h&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">unused_admin</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">condition</span>: <span style="color:#f1fa8c">&#34;permission contains admin and last_used_at &lt; now - 30d&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">action</span>: <span style="color:#f1fa8c">&#34;owner_review&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">sla</span>: <span style="color:#f1fa8c">&#34;7d&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">org_mismatch</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">condition</span>: <span style="color:#f1fa8c">&#34;subject.department != resource.owner_department&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">action</span>: <span style="color:#f1fa8c">&#34;owner_review&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">sla</span>: <span style="color:#f1fa8c">&#34;7d&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">stale_api_key</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">condition</span>: <span style="color:#f1fa8c">&#34;subject_type == api_key and last_used_at &lt; now - 90d&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">action</span>: <span style="color:#f1fa8c">&#34;rotate_or_revoke&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">sla</span>: <span style="color:#f1fa8c">&#34;14d&#34;</span>
</span></span></code></pre></div><p>후보가 너무 많으면 실패합니다. 첫 달에는 critical/high만 대상으로 삼고, 회수 후보가 owner당 주 20개를 넘지 않게 제한하는 편이 좋습니다. 나머지는 통계로만 보고 기준을 다듬습니다.</p>
<p><strong>4단계: 회수 자동화와 감사 증거</strong></p>
<p>회수 이벤트는 단순 update가 아니라 감사 이벤트입니다. 최소 아래를 남깁니다.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>event: permission.revoked
</span></span><span style="display:flex;"><span>grant_id: pg_123
</span></span><span style="display:flex;"><span>subject_id: user_456
</span></span><span style="display:flex;"><span>permission: billing:admin
</span></span><span style="display:flex;"><span>resource_id: tenant_789
</span></span><span style="display:flex;"><span>revoked_by: access_review_job
</span></span><span style="display:flex;"><span>review_owner: team-billing
</span></span><span style="display:flex;"><span>reason_code: unused_45d_expired
</span></span><span style="display:flex;"><span>previous_last_used_at: 2026-05-21T03:10:00Z
</span></span><span style="display:flex;"><span>policy_version: access-review-2026-07
</span></span></code></pre></div><p>이 기록은 <a href="/learning/deep-dive/deep-dive-tamper-evident-audit-log-playbook/">Tamper-Evident Audit Log</a>의 high-risk 액션으로 봐야 합니다. 권한 회수가 잘못됐을 때 되돌리기 위해서도, 회수가 필요했음을 설명하기 위해서도 증거가 필요합니다.</p>
<h3 id="2-의사결정-기준숫자조건우선순위">2) 의사결정 기준(숫자·조건·우선순위)</h3>
<p>초기 운영 기준은 아래처럼 잡을 수 있습니다.</p>
<table>
  <thead>
      <tr>
          <th>항목</th>
          <th>권장 출발선</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>critical 권한 만료 누락률</td>
          <td>0%</td>
      </tr>
      <tr>
          <td>high-risk grant 리뷰 주기</td>
          <td>30~90일</td>
      </tr>
      <tr>
          <td>break-glass 기본 만료</td>
          <td>24시간, 최대 72시간</td>
      </tr>
      <tr>
          <td>admin 권한 미사용 회수 후보</td>
          <td>30일</td>
      </tr>
      <tr>
          <td>일반 권한 미사용 회수 후보</td>
          <td>90~180일</td>
      </tr>
      <tr>
          <td>파트너/API Key 미사용 회수 후보</td>
          <td>90일</td>
      </tr>
      <tr>
          <td>회수 후 stale allow 반영</td>
          <td>critical 1분, high 5분</td>
      </tr>
      <tr>
          <td>owner 미응답 자동 처리</td>
          <td>high는 suspend, low는 재알림 후 만료</td>
      </tr>
  </tbody>
</table>
<p>우선순위는 <strong>외부 노출/민감 데이터 &gt; 권한 변경 가능 권한 &gt; production write &gt; 결제/정산 &gt; 일반 읽기</strong> 순서가 안전합니다. 모든 권한을 같은 강도로 관리하려고 하면 팀이 지칩니다. 먼저 사고 비용이 큰 권한부터 닫아야 합니다.</p>
<h3 id="3-운영-대시보드">3) 운영 대시보드</h3>
<p>Access Review가 잘 도는지 보려면 결과 지표가 있어야 합니다.</p>
<ul>
<li>active high-risk grant 수</li>
<li>owner 없는 grant 비율</li>
<li>expires_at 없는 high-risk grant 수</li>
<li>30/90/180일 미사용 권한 수</li>
<li>review candidate 생성 수와 실제 revoke 비율</li>
<li>owner SLA 초과 건수</li>
<li>회수 후 403 증가율</li>
<li>revoked credential 재사용 시도</li>
</ul>
<p>여기서 <code>revoke 비율</code>만 높이는 것은 목표가 아닙니다. 너무 공격적으로 회수하면 운영 장애가 납니다. 좋은 목표는 owner 없는 권한과 만료 없는 high-risk 권한을 줄이고, 회수 후 장애를 낮게 유지하는 것입니다.</p>
<h2 id="트레이드오프주의점">트레이드오프/주의점</h2>
<p>첫째, 자동 회수는 보안을 높이지만 업무를 끊을 수 있습니다. 특히 오래된 배치, partner integration, 고객 지원 도구는 사용 빈도가 낮아도 필요할 수 있습니다. 그래서 high-risk는 짧게, low-risk는 유예를 두는 위험도 기반 정책이 필요합니다.</p>
<p>둘째, owner 정보를 믿기 어렵습니다. 팀 이동과 조직 개편이 권한 테이블보다 빠르게 변합니다. HR/IdP/프로젝트 관리 도구와 동기화하되, 최종 회수 책임자는 사람이 확인할 수 있게 남겨야 합니다.</p>
<p>셋째, 권한 검토가 너무 잦으면 모두가 형식적으로 승인합니다. critical/high는 30~90일, medium은 6개월, low는 이벤트 기반으로 나누는 편이 낫습니다. 검토 주기는 짧을수록 좋은 것이 아니라, 실제 판단 가능한 단위여야 합니다.</p>
<p>넷째, 감사 로그에 민감정보를 많이 넣으면 또 다른 보안 문제가 됩니다. 권한 회수 이벤트에는 원문 고객 데이터나 secret을 넣지 말고, grant id, subject id, reason code, policy version, digest 중심으로 남기는 것이 안전합니다. 이 지점은 <a href="/learning/deep-dive/deep-dive-data-retention-deletion-architecture/">데이터 보존·삭제 아키텍처</a>와 함께 봐야 합니다.</p>
<p>다섯째, Access Review는 인가 버그를 대신하지 않습니다. 정책 자체가 잘못됐거나, 코드 경로가 PDP를 우회하거나, 캐시 무효화가 안 되면 리뷰로 해결되지 않습니다. 리뷰는 사후 정리와 drift 축소 장치이고, 실시간 판정 품질은 별도 테스트와 관측으로 지켜야 합니다.</p>
<h2 id="체크리스트-또는-연습">체크리스트 또는 연습</h2>
<h3 id="체크리스트">체크리스트</h3>
<ul>
<li><input disabled="" type="checkbox"> high-risk permission 목록이 문서화되어 있다.</li>
<li><input disabled="" type="checkbox"> 권한 grant마다 owner, reason, source, expires_at, last_used_at이 있다.</li>
<li><input disabled="" type="checkbox"> break-glass와 임시 권한은 기본 만료를 가진다.</li>
<li><input disabled="" type="checkbox"> 조직 이동, 그룹 탈퇴, 프로젝트 종료 이벤트가 권한 검토 후보로 연결된다.</li>
<li><input disabled="" type="checkbox"> Access Review 후보는 위험도와 사용 이력으로 우선순위가 매겨진다.</li>
<li><input disabled="" type="checkbox"> owner 미응답 시 suspend/revoke/재알림 기준이 정해져 있다.</li>
<li><input disabled="" type="checkbox"> 권한 회수 이벤트가 감사 로그로 남는다.</li>
<li><input disabled="" type="checkbox"> 회수 후 권한 캐시, 세션, API Key 검증 경로가 즉시 또는 TTL 내 반영된다.</li>
<li><input disabled="" type="checkbox"> 회수 후 403 증가와 job failure를 모니터링한다.</li>
<li><input disabled="" type="checkbox"> owner 없는 권한과 만료 없는 high-risk 권한을 월간 지표로 본다.</li>
</ul>
<h3 id="연습">연습</h3>
<ol>
<li>현재 서비스의 권한 부여 경로를 모두 적어 보세요. 관리자 UI, DB script, migration, IdP group, API Key 발급, 긴급 대응 절차 중 하나라도 빠지면 인벤토리를 다시 봐야 합니다.</li>
<li><code>billing:admin</code>, <code>customer_pii:export</code>, <code>project:write</code> 세 권한을 예로 들어 risk tier, 기본 만료, 리뷰 주기, 회수 SLA를 표로 정해 보세요.</li>
<li>최근 90일 동안 사용되지 않은 admin 권한을 찾는 SQL 또는 배치 로직을 설계해 보세요. 단, 바로 삭제하지 말고 owner 알림, 유예 기간, 회수 후 모니터링까지 포함해야 합니다.</li>
<li>권한 회수 뒤 캐시가 30분 동안 stale allow를 유지하는 상황을 가정하고, 어떤 API가 가장 위험한지와 TTL/이벤트 무효화 기준을 정해 보세요.</li>
</ol>
<h2 id="관련-글">관련 글</h2>
<ul>
<li><a href="/learning/deep-dive/deep-dive-authorization-models-rbac-abac-rebac/">인가 모델 실전 설계: RBAC·ABAC·ReBAC</a></li>
<li><a href="/learning/deep-dive/deep-dive-authorization-decision-cache-invalidation-playbook/">권한 판정 캐시와 권한 무효화 플레이북</a></li>
<li><a href="/learning/deep-dive/deep-dive-api-key-lifecycle-rotation-revocation-playbook/">API Key Lifecycle 발급·회전·폐기 플레이북</a></li>
<li><a href="/learning/deep-dive/deep-dive-tamper-evident-audit-log-playbook/">Tamper-Evident Audit Log 운영 플레이북</a></li>
<li><a href="/learning/deep-dive/deep-dive-secret-management/">시크릿 관리 실무</a></li>
</ul>
]]></content:encoded></item></channel></rss>