권한 인식 RAG (Permission-Aware RAG): 검색 시점에 문서 ACL 강제 적용하기
요약
RAG 시스템 구축 시 보안을 위해 검색 단계에서 문서 접근 제어(ACL)를 강제하는 '권한 인식 RAG' 아키텍처를 제안합니다. 생성 후 필터링이나 청크 리스트 필터링의 한계를 지적하며, 검색 조건(Predicate)에 권한을 포함하여 정보 유출을 구조적으로 차단하는 방식을 설명합니다.
핵심 포인트
- 생성 후 필터링은 LLM을 통한 간접적인 정보 유출 위험이 있음
- 검색 결과에서 청크를 제거하는 방식은 검색 품질 저하 및 사이드 채널 공격에 취약함
- 검색 쿼리 자체에 권한 필터를 포함하여 데이터 존재 자체를 숨기는 것이 가장 안전함
- CI 과정에서 낮은 권한 사용자의 접근을 차단하는 회귀 테스트(Hard Gate)가 필수적임
대부분의 RAG 데모는 "없음" 상태의 보안 모델을 가지고 있습니다. 문서들은 하나의 공유 인덱스에 들어가며, 질문을 할 수 있는 사람이라면 누구든 어떤 문서에서든 콘텐츠를 끌어낼 수 있습니다. 컴플라이언스(Compliance)나 금융 도메인에서 이는 단순한 결함이 아니라, 도입 불가 사유가 됩니다.
제가 컴플라이언스 도메인을 위한 엔터프라이즈 코파일럿(Copilot)인 Atlas를 구축했을 때, 첫 번째 아키텍처 결정 사항은 액세스 제어(Access Control)가 어디에 위치하느냐였습니다. 여기에는 세 가지 옵션이 있으며, 그중 두 가지는 잘못된 방식입니다.
옵션 1 (잘못됨): 생성 후 필터링 (filter after generation)
답변을 먼저 생성한 다음, 사용자가 해당 소스를 볼 권한이 있었는지 확인하는 방식입니다. 그 시점에는 이미 LLM(Large Language Model)이 제한된 콘텐츠를 읽은 상태이며, 의역, 요약, 또는 거절 메시지 자체를 통해 정보를 유출할 수 있습니다 ("3분기 구조조정 메모에 대해서는 말씀드릴 수 없습니다..."라는 답변 자체가 유출입니다). 사후 필터링(Post-hoc filtering)은 보안 경계를 UX 문제처럼 취급하는 것입니다.
옵션 2 (잘못됨): 최종 청크 리스트 필터링 (filter the final chunk list)
공유 인덱스에서 top-k를 검색한 다음, 사용자가 접근할 수 없는 청크(Chunk)를 제거하는 방식입니다. 이전보다 낫지만, 검색 품질을 저하시킵니다. 즉, top-k 결과가 조용히 희석되며, 랭킹 점수는 사용자가 봐서는 안 될 코퍼스(Corpus)를 기준으로 계산되었습니다. 더 심각한 문제는 타이밍 및 점수 사이드 채널(Side-channels)을 통해 제한된 문서가 존재한다는 사실이 드러날 수 있다는 점입니다.
옵션 3: 검색 조건(Predicate)의 일부로 권한 포함하기
Atlas에서는 pgvector의 모든 청크가 인제스션(Ingestion) 시점에 작성된 메타데이터로서 권한 라벨(public | analyst | compliance | restricted)을 보유합니다. 검색은 랭킹을 매기기 전에 호출자의 검증된 권한에 따라 필터링을 수행합니다. 이것이 밀집 검색(Dense Retrieval) 쿼리의 실제 형태입니다 (희소 tsvector 검색도 동일한 조건 빌더를 공유합니다):
SELECT id, document_id, content, clearance, metadata,
(1 - (embedding <=> ?::vector)) AS score
FROM atlas_chunk
...
복사해둘 만한 세부 사항 하나는 다음과 같습니다. 밀집 kNN (dense kNN)과 희소 전체 텍스트 (sparse full-text) 검색 경로 모두 **단일 공유 RBAC 필터 빌더 (single shared RBAC filter builder)**로부터 WHERE 절을 생성하므로, 신뢰 경계 (trust boundary)가 정확히 하나만 존재하며 이를 우회할 수 없습니다. 술어 (predicate)는 SQL 내부로 푸시(push)되며, 선택적인 사후 필터 (post-filter)로 적용되지 않습니다.
이를 통해 얻는 속성은 다음과 같습니다. 권한 수준 간 정보 유출 (cross-clearance leakage)이 "테스트 대상"이 아니라, _구조적으로 불가능_해집니다. 제한된 문서는 순위가 낮아지거나 필터링되는 것이 아니라, 쿼리 관점에서는 아예 존재하지 않는 것이 됩니다.
모두가 건너뛰는 부분: 이것이 계속 유지됨을 증명하기
회귀 게이트 (regression gate)가 없다면 설계의 가치는 거의 없습니다. Atlas는 CI(지속적 통합) 과정에 **부정 접근 하드 게이트 (negative-access hard gate)**를 갖추고 있습니다. 이는 낮은 권한을 가진 사용자가 제한된 문서만을 정답 소스로 하는 질문을 던지는 평가 스위트 (eval suite)입니다. 통과 시의 동작은 근거 있는 거절 (grounded refusal)이며, 유출이 발생하면 게이트는 빌드를 실패 처리합니다. 이 과정은 커밋된 카세트 (committed cassettes)를 대상으로 GPU 없이 실행되므로, 단위 테스트 (unit test)처럼 모든 머지 (merge)를 제어합니다.
중요한 두 가지 구현 참고 사항은 다음과 같습니다:
- 권한 (Clearance)은 요청이 아니라 검증된 신원 (identity)으로부터 옵니다. 게이트웨이는 독립적으로 검증된 내부 JWT를 단언(assert)합니다. RAG 엔진은 해당 단언으로부터 권한을 해결하며, 유효한 단언이 존재할 경우 클라이언트가 제공한 권한 헤더는 무시합니다. (만약 공격자가 파라미터로
clearance=SECRET을 전달할 수 있다면, 당신의 코퍼스 (corpus)를 탈취한 것과 다름없습니다.) - 시맨틱 캐시 (semantic cache)는 권한별로 파티셔닝됩니다. 높은 권한을 가진 사용자를 위해 계산된 캐시된 답변은 절대로 낮은 권한을 가진 사용자에게 제공되어서는 안 됩니다. 캐시 키에는 권한 계층 (clearance tier)이 포함됩니다. 캐싱은 "단순히 성능을 위한 것"이기 때문에 이 유출 경로는 놓치기 쉽습니다.
요약 (Takeaway)
10년간의 백엔드 작업은 권한 부여 (authorization)가 사후에 덧붙여지는 것이 아니라 데이터 경로 (data path)에 속해야 한다는 것을 가르쳐 주었습니다. RAG는 그 규칙을 바꾸지 않습니다. 단지 그 규칙을 잊어버리기 쉬운 새로운 데이터 경로를 제공할 뿐입니다. ACL을 검색 술어 (retrieval predicate)에 넣고, 누군가 이를 깨뜨렸을 때 빌드를 실패시키는 평가 (eval)를 작성하십시오.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기