에이전트는 여러분이 완전히 예측할 수 없는 호출자이며, 바로 그런 호출자야말로 최소 권한을 가져야 한다. 새 에이전트를 만들 때의 본능은 반복 작업을 하는 동안 "일단 돌아가게" 넓은 역할을 부여하는 것이다. 그 본능이 바로 언어 모델이 모든 버킷을 읽고 모든 테이블을 삭제할 수 있는 자격증명을 쥔 채, 공격자가 영향을 미칠 수 있는 텍스트에 의해 움직이게 만드는 방식이다. 최소 권한은 언제나 원칙이었다. 비결정적인 호출자는 그것을 타협 불가능하게 만든다.

사고방식의 전환은 에이전트를 여러분의 코드로 여기는 것을 멈추고, 여러분을 대신해 실행하는 반쯤 신뢰된 행위자로 여기는 데서 시작된다. 여러분의 코드는 여러분이 작성한 대로 동작한다. 에이전트는 모델이 결정한 대로 동작하고, 그 결정은 여러분이 통제할 수 없는 입력에 근거한다. IAM 경계는 "에이전트가 이상한 선택을 했다"와 "에이전트가 관리자 권한으로 이상한 선택을 했다" 사이에 서 있는 것이다.

에이전트당 역할 하나가 아니라 도구당 역할 하나

흔한 안티패턴은 에이전트 전체에 연결된 단일 실행 역할이 모든 도구가 필요로 할 수 있는 모든 권한의 합집합을 짊어지는 것이다. 그 역할은 이제 모든 도구의 피해 반경을 한꺼번에 짊어진 셈이다. 가장 약한 도구에 도달한 프롬프트 인젝션이 가장 강력한 도구의 권한을 물려받는다.

권한은 에이전트가 아니라 도구 단위로 범위를 지정하라. lookup_order 도구 뒤의 Lambda는 테이블 하나만 읽을 수 있는 역할을 갖는다. send_email 뒤의 함수는 검증된 발신 ID 하나에 대해서만 SES를 호출할 수 있는 역할을 갖는다. 어떤 도구도 쓰지 않는 권한을 짊어지지 않으므로, 침해된 도구 호출은 그 도구의 한 가지 임무만 할 수 있다.

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": "dynamodb:GetItem",
    "Resource": "arn:aws:dynamodb:eu-west-1:111122223333:table/orders",
    "Condition": {
      "ForAllValues:StringEquals": {
        "dynamodb:LeadingKeys": ["${aws:PrincipalTag/tenant}"]
      }
    }
  }]
}

이것이 주문 조회 도구의 권한 전부다: 테이블 하나에서 항목 하나를 읽되, 호출자의 테넌트에 속한 행만 읽는다. 인젝션된 지시문이 도달할 곳이 그 외에는 없다.

세션 정책으로 런타임에 역할을 더 좁혀라

도구 단위 역할은 바닥선이다. 멀티테넌트 시스템에서는 권한을 특정 요청 단위로도 범위를 좁히고 싶을 것이다. 세션 정책이 그것을 가능하게 한다: 백엔드가 도구의 역할을 가정할 때, 그 세션 하나에 한해 역할의 권한과 교집합을 이루는 인라인 정책을 함께 전달할 수 있다. 그러면 테넌트 A를 위해 동작하는 요청이 역할 자체는 테이블 전체에 걸쳐 있더라도 테넌트 B의 데이터에 손댈 수 없다.

aws sts assume-role \
  --role-arn arn:aws:iam::111122223333:role/order-lookup \
  --role-session-name agent-tenant-a \
  --policy '{"Version":"2012-10-17","Statement":[{
    "Effect":"Allow","Action":"dynamodb:GetItem",
    "Resource":"arn:aws:dynamodb:eu-west-1:111122223333:table/orders",
    "Condition":{"ForAllValues:StringEquals":
      {"dynamodb:LeadingKeys":["tenant-a"]}}}]}'

실효 권한은 역할과 세션 정책의 교집합이므로, 도구가 실제로 쥐게 되는 자격증명은 이번 요청으로 좁혀진다. 역할에 세션 태그와 테넌트 조건을 함께 쓰면 같은 효과를 선언적으로 얻을 수 있다. 어느 쪽이든, 모델이 주도하는 호출에 넘겨지는 자격증명은 현재 작업을 완수하는 데 필요한 최소 집합이다.

자격증명 앞에 정책을 두어라

IAM은 프린시펄이 무엇을 할 수 있는지를 결정한다. 하지만 이 특정 에이전트가, 이 특정 턴에서, 이 도구를 애초에 호출해도 되는지는 결정하지 않는다. 그 인가 질문에는 이제 AWS에서 전용 답이 있다: 이번 달 초부터 정식 출시(GA)된 Amazon Bedrock AgentCore의 Policy는 여러분이 자연어로 작성한 규칙에 대해 모든 에이전트-도구 요청을 평가하며, 그 규칙은 Cedar로 컴파일되어 AgentCore Gateway에서 호출이 진행되기 전에 강제된다. 이것을 IAM의 대체물이 아니라 IAM 앞에 놓인 레이어로 이해하라: 게이트웨이는 도구 호출이 허용되는지를 결정하고, 촘촘한 IAM 역할은 그것이 허용된 경우 얼마나 작은 피해로 그칠지를 결정한다.

API 호출뿐 아니라 역할 가정(assumption)도 기록하라

도구가 범위 지정된 역할을 가정하기 때문에, CloudTrail은 깔끔한 감사 흔적을 남겨준다: 어떤 역할이, 어떤 세션 이름으로, 어떤 작업을 위해 가정되었는지. role-session-name을 에이전트 턴과 테넌트로 거슬러 올라갈 수 있는 값으로 설정하면, 에이전트가 정확히 무엇을 어떤 권한 아래 했는지를 재구성할 수 있다. 에이전트가 오작동했을 때, 그 흔적이 설명 가능한 범위 있는 사고와 풀리지 않는 미스터리를 가르는 차이가 된다.

핵심 요약

최소 권한은 호출자가 똑똑하다고 느슨해지지 않는다. 오히려 조여진다. 호출자가 비결정적이고 신뢰되지 않은 입력에 영향을 받기 때문이다. 각 도구에 좁은 역할을 별도로 부여하고, 요청마다 세션 정책과 교집합을 이루게 하고, AgentCore Policy 같은 인가 레이어를 앞에 두고, 모든 역할 가정을 기록하라. 모델이 잘못된 판단을 내릴 때, 그리고 언젠가는 반드시 그럴 때, 그 잘못된 판단이 작게 머물도록 결정하는 것이 IAM이다.

다음으로 읽을 글

IAM의 심화 영역과 계정 보안 강화는 클라우드 필드 노트에 있다: AWS 계정 보안, ercan.cloud에서 확인할 수 있다. 허브는 ercanermis.com이다.