30초 답변
기업 RAG의 접근제어는 로그인 화면이나 프롬프트 한 줄로 해결되지 않는다. 원본 문서의 ACL을 수집 단계에서 읽고, 문서에서 파생된 모든 청크에 권한 메타데이터를 상속한 뒤, 질의 시 인증된 사용자의 ID·그룹·역할을 서버가 검색 필터로 강제해야 한다. 권한 정보가 없거나 해석에 실패하면 공개로 간주하지 말고 검색 결과를 반환하지 않는 것이 기본값이어야 한다.
안전한 흐름은 다음과 같다.
원본 콘텐츠 + 원본 ACL → 권한 정규화 → 문서·청크 동시 색인 → 사용자 인증 → principal 해석 → 서버 측 ACL 필터 → 허용 후보만 검색·리랭킹 → 허용 근거만 LLM·인용·캐시에 사용
이 글의 핵심
- 인증은 “누구인가”를 확인하고, 인가는 “이 문서를 볼 수 있는가”를 결정한다. 둘을 분리해 설계한다.
- ACL은 부가 태그가 아니라 콘텐츠와 함께 생성·변경·삭제되어야 하는 보안 데이터다.
- 권한 필터는 검색 후 LLM에서 지우는 방식이 아니라 후보 검색 전에 또는 검색 엔진이 보장하는 동일 단계에서 적용한다.
- ACL 누락, 토큰 오류, 그룹 조회 실패, 정책 충돌에서는 deny-by-default로 실패해야 한다.
- 검색 결과뿐 아니라 제목 자동완성, 인용, 대화 기록, 캐시, 평가 데이터에도 같은 권한 경계를 적용한다.
먼저 구분해야 할 세 가지: 인증, 정책, 검색 필터
사용자가 회사 계정으로 로그인했다고 해서 모든 사내 문서를 볼 수 있는 것은 아니다. 애플리케이션은 인증된 토큰에서 사용자 식별자를 얻고, 신뢰할 수 있는 IAM 또는 디렉터리에서 그룹·역할 정보를 확인한 뒤, 원본 문서 정책과 비교해 접근을 결정해야 한다.
NIST의 Zero Trust Architecture는 네트워크 위치나 자산 소유만으로 암묵적 신뢰를 부여하지 않고 자원 접근 전에 인증과 인가를 수행하는 원칙을 설명한다. NIST SP 800-162는 주체, 객체, 요청 작업, 환경의 속성을 정책과 비교하는 ABAC를 정의한다. RAG에 적용하면 다음처럼 대응된다.
| 접근제어 요소 | RAG에서의 예 |
|---|---|
| 주체(subject) | 사용자 ID, 그룹, 직무, 테넌트 |
| 객체(object) | 문서 ID, 청크 ID, 부서, 보안 등급 |
| 작업(action) | 검색, 원문 열람, 요약, 다운로드 |
| 환경(environment) | 접속 채널, 기기 상태, 시간, 지역 |
| 정책(policy) | 허용 그룹, 명시적 거부, 만료일 |
검색 엔진의 메타데이터 필터는 이 정책을 실행하는 한 수단일 뿐이다. Microsoft의 security filter 패턴도 principal 문자열 비교 자체는 인증·인가가 아니라고 명확히 구분한다. 따라서 클라이언트가 보낸 group_ids를 그대로 믿지 말고 서버가 검증된 사용자 컨텍스트로 필터를 작성해야 한다.
ingestion 단계: ACL을 콘텐츠와 같은 원본에서 가져온다
1. 권한의 원천을 하나 정한다
SharePoint, 파일 서버, 문서관리시스템, 데이터베이스 중 무엇이 권한의 기준인지 문서 유형별로 정한다. 별도 엑셀에 접근 목록을 복사해 관리하면 원본 권한 변경과 색인이 쉽게 어긋난다. 수집기는 콘텐츠와 함께 문서 ID, 상위 폴더·컨테이너, 소유 테넌트, 허용 사용자·그룹, 명시적 거부, 정책 버전, 원본 수정 시각을 읽어야 한다.
2. principal을 변경되지 않는 ID로 정규화한다
사람 이름, 이메일 표시명, 부서명은 변경되거나 중복될 수 있다. IAM의 불변 사용자·그룹 ID를 저장하고, 표시명은 운영 화면용 별도 필드로 둔다. 그룹 중첩을 색인 시 펼칠지 질의 시 해석할지는 디렉터리 규모와 변경 빈도에 따라 정하되, 어느 시점의 멤버십을 사용했는지 추적할 수 있어야 한다.
3. 문서 권한을 모든 파생 청크에 상속한다
한 PDF가 30개 청크로 나뉘었다면 30개 모두 동일한 문서 ACL과 source_document_id를 가져야 한다. 표·이미지 설명·OCR 텍스트처럼 별도 파이프라인에서 생성된 파생물도 예외가 아니다. 부모 문서가 삭제되거나 권한이 바뀌면 자식 청크를 모두 찾아 갱신할 수 있도록 결정적인 ID 체계를 사용한다.
권장 최소 필드는 다음과 같다.
{
"chunk_id": "tenant-a:doc-184:v7:chunk-003",
"source_document_id": "tenant-a:doc-184",
"tenant_id": "tenant-a",
"allow_user_ids": [],
"allow_group_ids": ["group-hr-policy"],
"deny_user_ids": [],
"classification": "internal",
"permission_version": "acl-20260717-09",
"source_updated_at": "2026-07-17T01:10:00Z",
"permission_updated_at": "2026-07-17T01:12:00Z"
}
필드 이름과 형식은 제품마다 다르지만, 콘텐츠 버전과 권한 버전을 구분하는 것이 중요하다. 문장은 그대로인데 권한만 변경되는 사건도 있기 때문이다.
4. 빈 ACL의 의미를 공개로 두지 않는다
수집기가 ACL을 읽지 못했는데 빈 배열을 “전체 공개”로 해석하면 가장 위험한 종류의 장애가 된다. 공개 문서는 visibility: public처럼 명시적으로 표시하고, 그 외 문서에서 ACL이 비어 있으면 색인을 격리하거나 검색 불가 상태로 둔다. 파싱 오류·지원하지 않는 principal·상속 해석 실패도 운영 대기열로 보낸다.
5. 권한 변경을 별도 이벤트로 처리한다
문서 본문 해시가 같다는 이유로 재색인을 건너뛰면 퇴사자나 부서 이동자의 권한이 남는다. create, content update, permission change, move, delete 이벤트를 구분하고, 권한 변경은 임베딩을 다시 만들 필요가 없더라도 메타데이터 업데이트와 관련 캐시 무효화를 즉시 수행해야 한다. Azure의 검색 보안 모범 사례는 권한 메타데이터가 색인 시 캡처되고 질의 시 집행된다는 구조와, 일부 방식에서 원본 ACL 변경 후 재색인이 필요할 수 있음을 설명한다.
query 단계: 사용자가 아니라 서버가 필터를 만든다
1. 신뢰할 수 있는 사용자 컨텍스트를 만든다
서버는 토큰 서명, 발급자, 대상, 만료를 검증하고 사용자·테넌트 ID를 얻는다. 필요한 경우 디렉터리에서 현재 그룹 멤버십을 조회한다. 조회가 실패했을 때 이전 그룹을 무기한 사용하는 대신 짧고 명시적인 캐시 정책과 실패 시 거부 규칙을 둔다.
2. 비즈니스 필터와 ACL 필터를 AND로 결합한다
사용자의 “2026년 인사 규정만 보여줘”라는 조건은 year=2026 AND department=HR 같은 비즈니스 필터다. 서버가 강제하는 tenant_id와 ACL 필터를 여기에 반드시 AND로 결합한다. 사용자가 요청 본문에서 ACL 필드를 덮어쓰거나 필터를 제거할 수 없어야 한다.
개념적 정책은 다음과 같다.
same_tenant
AND explicitly_published
AND NOT explicitly_denied
AND (public OR user_allowed OR any_group_allowed)
명시적 거부를 운영한다면 허용보다 우선하도록 정책 순서를 고정한다. 공개 문서도 법무 보존, 폐기 예정, 사고 대응 등 별도 차단 사유가 있으면 제외할 수 있어야 한다.
3. 허용된 후보만 벡터 검색·리랭킹·생성에 사용한다
권한 없는 후보를 먼저 top-k로 가져온 뒤 애플리케이션에서 제거하면 허용 문서가 후보에 들어오지 못하는 문제가 생긴다. 더 중요한 문제는 리랭커나 LLM이 이미 비허용 내용을 처리할 수 있다는 점이다. 검색 제품이 pre-filter, query-time ACL enforcement, security trimming 중 어떤 방식을 보장하는지 확인한다. Azure의 문서 수준 접근제어 개요는 내장 ACL 방식과 문자열 기반 security trimming 패턴을 구분해 설명한다.
OpenAI Retrieval의 attribute filtering은 의미 검색 전에 파일 속성 조건을 적용하도록 제공된다. 이 기능을 ACL에 사용할 때도 속성값을 클라이언트 입력이 아니라 검증된 서버 컨텍스트에서 만들어야 한다.
4. 답변 이후의 표면도 권한 검사한다
출처 제목, 미리보기, 다운로드 URL, 자동완성, 최근 검색어, 관리자 분석 화면이 문서 존재 자체를 노출할 수 있다. 최종 인용 링크를 만들 때 현재 권한을 다시 확인하고, 원문 저장소도 자체 인가를 수행하게 한다. 검색 서비스에서 허용됐다는 이유만으로 서명된 원문 URL을 장기간 재사용하지 않는다.
5. 캐시 키에 권한 컨텍스트를 포함한다
질문 문자열만으로 검색 결과나 답변을 캐시하면 먼저 질문한 관리자의 결과가 일반 사용자에게 재사용될 수 있다. 최소한 테넌트, 정책 버전 또는 권한 집합 해시, 검색 구성 버전, 질문을 캐시 키에 포함한다. 권한 변경 이벤트가 오면 해당 문서가 들어간 검색·답변 캐시를 무효화한다. 권한 집합 전체를 로그에 평문으로 남기는 대신 추적 가능한 해시와 정책 판정 이유를 기록한다.
사용자 경험과 실패 시나리오
“로그인은 되는데 검색 결과가 하나도 없습니다”
보안상 빈 결과는 올바른 실패일 수 있지만 사용자는 장애로 느낀다. “접근 가능한 자료에서 답을 찾지 못했습니다”라고 안내하고, 권한 요청 경로 또는 검색 범위 확인 방법을 제공한다. 존재하면 안 되는 문서 제목을 힌트로 보여주지는 않는다.
“부서를 옮겼는데 이전 문서가 계속 보입니다”
그룹 멤버십 캐시, 색인의 ACL, 검색 결과 캐시 중 하나가 오래된 상태다. 인증 토큰 갱신만 확인하지 말고 permission_updated_at, 색인 반영 시각, 캐시 키의 권한 버전을 함께 추적한다. 권한 회수 SLA를 정의하고 그 시간을 넘는 항목을 경보로 만든다.
“답변에는 안 나오지만 출처 목록에 비공개 제목이 보입니다”
본문만 마스킹하고 인용 메타데이터를 별도 경로에서 가져온 경우다. 답변, 인용, 미리보기, 원문 열람을 하나의 허용 문서 집합에서 생성하고 최종 응답 직전에 문서 ID 기준으로 교차 검사한다.
“권한 필터를 붙였더니 검색 품질이 급격히 나빠졌습니다”
전체 top-k를 만든 후 권한 필터링해 후보가 거의 남지 않거나, 사용자의 그룹 해석이 불완전할 수 있다. 허용 문서 집합 안에서 검색이 수행되는지 확인하고, 제한 사용자를 위한 Recall@k 평가를 별도로 만든다. 보안을 풀어 품질을 회복하는 것은 해결책이 아니다.
“ACL 조회 서비스가 잠시 장애인데 일단 검색은 되게 할까요?”
권한을 확인할 수 없는 상태에서 검색을 허용하면 기밀 노출로 이어질 수 있다. 비공개 말뭉치는 결과를 거부하고 재시도를 안내한다. 공개로 명시된 별도 인덱스만 독립적으로 서비스하는 구조라면 공개 결과만 제공할 수 있다.
실무 체크리스트
- 문서 유형별 ACL의 신뢰 가능한 원천이 정의되어 있는가?
- 사용자·그룹을 표시명이 아닌 불변 ID로 저장하는가?
- 문서에서 파생된 모든 텍스트·표·이미지 청크가 부모 ACL을 상속하는가?
- 공개 상태가 명시적이며 빈 ACL을 공개로 해석하지 않는가?
- 콘텐츠 변경과 권한 변경을 별도 이벤트·버전으로 추적하는가?
- 클라이언트가 아닌 서버가 검증된 토큰으로 ACL 필터를 작성하는가?
- 테넌트·ACL 필터가 사용자 필터와 AND로 강제 결합되는가?
- 권한 없는 후보가 리랭커나 LLM에 전달되지 않는가?
- ACL·그룹 조회 실패 시 deny-by-default로 동작하는가?
- 명시적 deny가 allow보다 우선하는지 테스트했는가?
- 답변·인용·자동완성·원문 URL·대화 기록에 같은 권한 검사가 적용되는가?
- 검색·답변 캐시 키에 테넌트와 권한 컨텍스트가 포함되는가?
- 입사·부서 이동·퇴사·그룹 중첩·ACL 제거 시나리오를 회귀 테스트하는가?
- 권한 회수 반영시간과 실패 경보 기준이 운영 문서에 있는가?
자주 묻는 질문
Q1. 사용자별로 벡터 인덱스를 따로 만들어야 하나요?
대부분의 경우 사용자별 인덱스는 문서 중복, 갱신 비용, 권한 변경 관리 부담이 크다. 테넌트나 법적 격리 단위로 물리적 인덱스를 나누고 그 안에서 문서 수준 ACL 필터를 쓰는 방식이 일반적이다. 다만 규제·고객 계약·키 관리 요구가 물리적 분리를 요구하면 보안 경계가 우선이다.
Q2. 메타데이터 필터만 붙이면 접근제어가 끝나나요?
아니다. 필터 값의 출처가 신뢰 가능해야 하고, 필터가 검색 전 과정에 강제되어야 하며, 원문 저장소도 별도 인가를 수행해야 한다. 문자열 비교형 security trimming은 유용한 구현 패턴이지만 인증 자체를 대신하지 않는다.
Q3. 권한 변경 때 임베딩도 다시 생성해야 하나요?
본문이 같다면 보통 임베딩을 다시 계산할 필요는 없다. 그러나 모든 파생 청크의 ACL 메타데이터를 갱신하고 검색·답변 캐시를 무효화해야 한다. 사용 중인 검색 제품이 권한 필드 부분 갱신을 지원하는지, 내장 ACL 기능이 재색인을 요구하는지는 해당 버전의 공식 문서를 확인한다.
근거 자료
- Microsoft Learn, Azure AI Search 문서 수준 접근제어 개요
- Microsoft Learn, 질의 시점 ACL·RBAC 집행
- Microsoft Learn, security filter를 이용한 검색 결과 제한
- Microsoft Learn, Azure AI Search 보안 모범 사례
- OpenAI Developers, Retrieval의 attribute filtering
- NIST, SP 800-207 Zero Trust Architecture
- NIST, SP 800-162 Attribute Based Access Control