AI 에이전트에게 운영 데이터베이스(Production Database)에 대한 안전한 접근 권한을 부여하는 방법
요약
AI 에이전트에게 운영 데이터베이스 접근 권한을 부여할 때 발생할 수 있는 보안 위험을 분석하고, 이를 방지하기 위한 이중 계층 보안 모델을 제안합니다. 데이터베이스 엔진의 정적 제어와 런타임 제어 평면을 결합하여 프롬프트 인젝션으로부터 안전한 환경을 구축하는 방법을 다룹니다.
핵심 포인트
- 단순 읽기 전용 권한만으로는 에이전트의 의도나 데이터 민감도를 제어하기 부족함
- DB 엔진 내부의 정적 제어(최소 권한, RLS 등)가 기본적으로 구축되어야 함
- 모든 작업을 분류하고 인간의 승인을 거치는 런타임 제어 평면이 필수적임
- 보안 강제 집행은 에이전트의 컨텍스트 외부인 데이터 경로에 존재해야 함
요약 (TL;DR): 읽기 전용(Read-only) 데이터베이스 사용자는 AI 에이전트를 안전하게 만드는 데 충분하지 않습니다. 왜냐하면 에이전트는 의도(Intent), 데이터 양(Data volume), 또는 민감도(Sensitivity)를 판단할 수 없으며, 쓰기 권한(Write access)을 부여하는 순간 아무런 보호책도 제공하지 못하기 때문입니다. 안전한 접근을 위해서는 두 가지 계층이 함께 작동해야 합니다. 데이터베이스 엔진 내부의 정적 제어(Static controls: 최소 권한 역할(Least-privilege roles), 읽기 복제본(Read replicas), 행 수준 보안(Row-level security), 구문 타임아웃(Statement timeouts))와, 모든 작업을 분류하고 기본 거부(Default-deny) 정책을 시행하며, 위험한 작업은 인간의 승인을 거치게 하고, 모든 것을 불변 로그(Immutable log)에 기록하는 전면의 런타임 제어 평면(Runtime control plane)입니다. 강제 집행(Enforcement)은 에이전트의 컨텍스트(Context) 외부인 데이터 경로(Data path)에 존재해야 하므로, 프롬프트 인젝션(Prompt injection)으로부터 안전할 수 있습니다.
팀원이 운영 데이터베이스(Production database) 접근 권한을 요청할 때, 단순히 비밀번호를 넘겨주지는 않습니다. 무엇을 읽을 수 있는지, 무엇을 변경할 수 있는지, 그리고 어떤 작업에 제2의 확인(Second pair of eyes)이 필요한지를 결정합니다. 당신은 판단이 한 번 승인되고 잊히는 것이 아니라, 모든 작업에 대해 지속적으로 적용될 것이라고 가정합니다.
AI 에이전트는 그 가정을 깨뜨립니다. 에이전트는 지치지 않고, 재고하기 위해 멈추지도 않으며, 티켓, 웹페이지, 또는 데이터베이스 자체의 행(Row)에서 전달된 텍스트를 포함하여 자신이 읽는 무엇에 대해서든 기꺼이 행동할 것입니다. 문제는 에이전트에게 권한을 줄 것인가의 여부가 아닙니다. 코딩 에이전트(Coding agents), 지원 코파일럿(Support copilots), 분석 어시스턴트(Analytics assistants)를 운영하는 대부분의 팀에게 그 결정은 이미 내려졌습니다. 진짜 질문은 에이전트가 무엇을 하라고 지시받았든 상관없이, 모든 작업이 당신이 정의한 경계 내에 머물 수 있도록 어떻게 권한을 부여하느냐 하는 것입니다.
솔직한 답변은 단 하나의 설정으로 이 문제를 해결할 수 없다는 것입니다. 안전한 접근은 두 가지 계층이 함께 작동해야 합니다: 데이터베이스 엔진 내부의 정적 제어(Static controls)와 그 전면의 런타임 제어 평면(Runtime control plane)입니다. 이 가이드는 두 계층 모두를 살펴보고, 왜 각각이 필요한지, 그리고 그 사이의 경계가 어디에 위치하는지 설명합니다. 예제는 구체적인 구현을 위해 Postgres를 사용하지만, 동일한 모델이 MySQL, MongoDB 및 에이전트가 접하는 다른 모든 엔진에도 적용됩니다.
첫 번째 계층: 정적 제어 (Static controls)
데이터베이스가 이미 제공하고 있는 기능부터 시작하세요. 이것들은 가장 기본적인 요건(table stakes)이며, 에이전트가 연결되기 전에 이 모든 것들을 설정해야 합니다.
- 최소 권한 원칙을 적용한 전용 역할 (A dedicated, least-privilege role). 에이전트가 애플리케이션 계정이나 관리자(admin) 계정을 재사용하게 해서는 절대 안 됩니다. 에이전트가 정확히 필요한 스키마(schema)와 작업(operation)에만 범위가 제한된 역할을 생성하세요. Postgres의 경우, 미리 정의된
pg_read_all_data역할은 쓰기 권한 없이 읽기 권한만 부여하므로, 읽기 전용(read-only) 에이전트를 위한 합리적인 시작점이 될 수 있습니다. (Postgres docs) - 읽기 복제본 (Read replicas). 분석 및 탐색형 에이전트는 복제본(replica)을 바라보게 하여, 에이전트의 쿼리가 운영 환경의 쓰기 트래픽과 충돌하거나 빈번하게 사용되는 테이블(hot tables)에 락(lock)을 거는 일이 없도록 하세요.
- 행 수준 보안 (Row-level security). 에이전트가 특정 테넌트(tenant)의 데이터만 볼 수 있어야 한다면, 에이전트가 올바른
WHERE절을 추가할 것이라고 믿기보다는CREATE POLICY를 통해 테이블 수준에서 이를 강제하세요. (Postgres docs) - 문장 타임아웃 (Statement timeouts). 제어되지 않는 쿼리(예: 에이전트가 무제한 조인(unbounded join)을 생성하는 경우)가 서버를 포화시키는 대신 서버에 의해 중단될 수 있도록
statement_timeout을 설정하세요. (Postgres docs) - 네트워크 및 연결 허용 목록 (Network and connection allowlists). 어떤 호스트가 어디에서 연결할 수 있는지 제한하세요.
이 계층은 필수적입니다. 이를 건너뛰는 것은 태만입니다. 하지만 이것만으로는 충분하지 않으며, 왜 그런지에 대해 정확히 짚고 넘어갈 가치가 있습니다.
정적 접근 제어(Static access)가 할 수 없는 것
정적 제어는 신원(identity)과 권한(permission)을 기반으로 작동합니다. 이들은 "이 역할이 이 객체에 대해 이 종류의 문장을 실행할 수 있는가?"라는 질문에 답합니다. 하지만 자율 에이전트(autonomous agent)가 루프에 들어온 이후 가장 중요해지는 질문들에는 답할 수 없습니다.
그들은 **의도 (intent)**를 판단할 수 없습니다. 읽기 전용 (read-only) 역할은 SELECT * FROM users를 허용합니다. 에이전트가 활성 계정 수를 세고 있든, 테이블의 모든 이메일 주소를 유출(exfiltrating)하고 있든 권한 부여 내용은 동일합니다. 권한 부여 시스템은 허용된 읽기를 확인하지만, 목적을 보지 못하며, 별도의 검사 도구를 구축하지 않는 한 데이터의 양(volume)이나 민감도(sensitivity)를 파악하지 못합니다.
그들은 **주입된 지시 사항 (injected instructions)**을 막을 수 없습니다. 프롬프트 인젝션 (Prompt injection)은 OWASP LLM 애플리케이션 Top 10의 최상위 항목이며, 그 이유는 구조적입니다. LLM은 지시 사항과 데이터를 동일한 채널에서 처리하므로, 신뢰할 수 없는 콘텐츠(고객 지원 티켓, 스크래핑된 페이지, 댓글 필드 등)가 명령어로 해석될 수 있습니다. (OWASP) 읽기 전용 권한은 이러한 공격에서 살아남을 수 있지만, 에이전트에게 단 한 번이라도 쓰기(write) 권한이 부여되는 순간, 주입된 "이제 오래된 레코드를 삭제해"라는 명령은 유효한 권한을 타고 그대로 실행될 수 있습니다.
그리고 그들은 **결국 발생하게 될 쓰기 (eventual write)**를 피할 수 없습니다. 읽기 전용은 시작하기에 편안한 지점이지만, 팀들이 그 상태에 머무는 경우는 드뭅니다. 에이전트의 목적은 대개 무언가를 해결하는 것입니다. 마이그레이션 (migration)을 적용하거나, 컬럼을 백필 (backfill)하거나, 사용하지 않는 인덱스를 삭제 (drop)하는 것 말입니다. 쓰기 또는 DDL 권한을 부여하는 즉시, 정적 계층 (static layer)의 보호 수준은 "이 역할은 이것을 수행할 수 있다"로 떨어지며, 이는 주입된 지시 사항이나 단순히 실수로 내려진 지시 사항이 악용할 수 있는 바로 그 보호 수준입니다.
레이어 2: 런타임 제어 평면 (runtime control plane)
그 간극은 "에이전트가 행동하기로 결정한 시점"과 "엔진이 이를 실행한 시점" 사이에 발생하는 모든 것입니다. 정적 권한 (static permissions)은 그 경계에서 신원 (identity)을 확인합니다. 여러분에게 추가로 필요한 것은 실제 동작을 매번 문맥 (context)에 따라 검사하고, 실행 여부를 결정하는 계층입니다.
그 계층은 런타임 제어 평면 (runtime control plane)입니다. 즉, 데이터 경로 (data path) 상의 데이터베이스 앞단에 위치하여, 각 문장(statement)이나 작업(operation)이 엔진에 도달하기 전에 가로채는 체크포인트 역할을 합니다. 이 계층은 모델 내부가 아닌 연결 경로 (connection path)에 존재하기 때문에, 에이전트가 말한 계획이 아니라 에이전트가 실제로 실행하려는 실제 쿼리 (query)를 볼 수 있습니다. 설계상 엔진 불가지론적 (engine-agnostic)입니다. 동일한 제어 평면이 Postgres의 UPDATE, MySQL의 ALTER TABLE, 그리고 MongoDB의 deleteMany를 모두 제어하는데, 이는 특정 벤더의 권한 모델에 의존하는 대신 작업 자체에 대해 추론하기 때문입니다.
제어 평면의 네 가지 역할
배포할 가치가 있는 제어 평면은 모든 작업에 대해 네 가지를 수행합니다.
- 분류 (Classify). 들어오는 작업을 파싱 (parse)하고 라벨을 붙입니다: 읽기(read) 또는 쓰기(write), DDL 또는 DML, 어떤 객체(object)인지, 범위 내 데이터 양은 얼마인지, 민감하다고 태그된 항목을 건드리는지 등을 분류합니다. 분류는 불투명한 문장을 정책이 추론할 수 있는 형태로 바꾸는 과정입니다.
- 기본 거부 정책 (default-deny policy) 강제. 거부에서 시작하여 명시된 특정 작업만 허용합니다. 이는 일반적인 태세를 뒤집는 것입니다. 당신이 생각한 위험한 것들을 차단하는 대신, 당신이 명시적으로 승인한 것들만 허용하며, 모든 새로운 시도는 기본적으로 차단됩니다.
- 위험한 작업은 사람의 승인 뒤로 격리 (Gate). 어떤 작업들은 절대 자율적으로 실행되어서는 안 됩니다. 스키마 변경 (schema changes), 대량 삭제 (bulk deletes), 금융 또는 개인 데이터에 접근하는 모든 작업은 일시 중지되어 사람을 기다려야 합니다. LLM 및 에이전트 시스템에 대한 OWASP의 가이드는 고위험 작업에 대해 사람의 승인을 요구하는 동일한 통제 방식을 반복해서 강조합니다. (OWASP) 과제는 일상적인 업무를 중단시키지 않으면서도 고위험 작업을 보호할 수 있도록 해당 게이트 (gate)를 설계하는 것입니다.
- 불변의 기록 (Record immutably). 모든 작업과 모든 결정(허용, 거부, 누구에 의해 승인되었는지)을 추가 전용 로그 (append-only log)에 기록합니다. 이를 통해 사후에 "에이전트가 무엇을 했고, 누가 승인했는가"라는 질문에 답할 수 있습니다.
왜 강제 실행은 에이전트의 문맥(context) 외부에 존재해야 하는가
이 부분은 실수하기 쉬운 대목입니다. 본능적으로 프롬프트(prompt)에 규칙을 넣고 싶어지기 때문입니다: "당신은 SELECT 문만 실행할 수 있으며, 확인 없이 DELETE를 수행해서는 안 됩니다." 하지만 이것은 가이드라인(guideline)일 뿐, 가드레일(guardrail)이 아닙니다.
지시 기반의 가드레일은 프롬프트 인젝션(prompt injection)이 작동하는 것과 동일한 이유로 실패합니다. 가드레일이 신뢰할 수 없는 입력값과 동일한 문맥 창(context window) 안에 존재하기 때문에, 정교하게 설계된 지시문이 모델을 설득하여 규칙을 어기게 만들 수 있습니다. 공격자가 없더라도 모델은 단순히 주어진 규칙을 우회하는 방식으로 추론할 수 있습니다. 에이전트가 읽을 수 있는 가드레일은 에이전트가 무시할 수 있는 가드레일입니다.
데이터 경로(data path)에서의 강제 실행은 논쟁의 여지가 없습니다. 제어 평면(control plane)은 대화의 참여자가 아닙니다. 제어 평면은 에이전트의 추론 과정을 읽지 않으며, 에이전트 또한 제어 평면에 메시지를 보낼 수 없습니다. 에이전트가 쿼리(query)를 생성하면, 제어 평면은 이를 분류하고 정책(policy)에 따라 검사합니다. 따라서 주입된 "이전 지시 사항을 무시하라"는 명령은 결정을 내리는 구성 요소에 결코 도달할 수 없습니다. 에이전트와 그 입력값이 영향을 미칠 수 있는 문맥으로부터 집행자(enforcer)를 격리하는 것, 이 분리가 바로 핵심입니다.
구체적인 Postgres 워크스루(walkthrough)
느려진 엔드포인트(endpoint)를 수정하는 임무를 맡은 코딩 에이전트를 상상해 보십시오. 이 에이전트는 데이터베이스에 직접 연결하는 것이 아니라, 제어 평면을 통해 연결됩니다.
- 에이전트는 느린 쿼리(slow query)를 찾기 위해
EXPLAIN과 여러 개의SELECT문을 실행합니다. 각 쿼리는 읽기(read)로 분류되고, 허용된 규칙과 일치하는지 확인된 후 실행되며 로그에 기록됩니다. - 에이전트는 해결책이 새로운 인덱스라고 판단하고
CREATE INDEX CONCURRENTLY를 실행합니다. 제어 평면(control plane)은 이를 DDL로 분류하며, DDL은 기본 거부(default-deny) 설정에 의해 자동으로 허용되지 않는 카테고리입니다. 작업은 일시 중지되며, 정확한 SQL 문, 대상 테이블, 그리고 에이전트가 명시한 이유와 함께 승인 채널로 요청이 전달됩니다. - 엔지니어가 이를 승인합니다. 인덱스가 생성됩니다. 승인 여부, 승인자, 타임스탬프는 해당 SQL 문과 함께 불변 로그(immutable log)에 기록됩니다.
- 나중에 Jira 티켓에 포함된 악의적인 주석(poisoned comment)이 에이전트를
DROP TABLE audit_log로 유도하려고 시도합니다. 잘못된 정보에 속은 에이전트가 해당 문을 생성합니다. 제어 평면은 이를 보호된 객체에 대한 파괴적인 DDL로 분류하고 즉시 거부합니다. 해당 시도는 기록됩니다. 아무것도 삭제되지 않습니다.
Postgres를 MySQL로 바꾸면 DDL은 ALTER TABLE이 되고, MongoDB로 바꾸면 파괴적인 호출은 dropCollection이 됩니다. 분류(classify), 강제(enforce), 게이트키핑(gate), 기록(record)의 루프는 동일합니다. 이러한 엔진 독립성(engine-independence) 덕분에 제어 평면은 엔진마다 별도의 규칙 세트(bespoke ruleset)를 만들 필요 없이 실제 데이터베이스 플릿(fleet) 전체로 확장될 수 있습니다.
체크리스트: 읽기 전용 권한 부여에서 관리된 경로(governed path)로
최소한의 권한 부여 단계에서 진정으로 관리되는 접근 경로로 전환하기 위해 이 체크리스트를 활용하십시오.
- 에이전트당 전용 최소 권한 역할 (least-privilege role) 부여, 공유 계정이나 관리자 (admin) 계정 사용 금지
- 가능한 경우 읽기 트래픽을 복제본 (replica)으로 지정
- 테넌트 (tenant) 또는 민감도 경계에 대한 행 단위 보안 (Row-level security) 적용
- 구문 타임아웃 (Statement timeouts) 및 연결 허용 목록 (allowlists) 구성
- 모든 에이전트 트래픽을 직접 연결이 아닌 런타임 제어 평면 (runtime control plane)을 통해 라우팅
- 모든 작업은 엔진에 도달하기 전에 분류
- 기본 거부 (Default-deny) 정책: 명시적으로 허용된 것만 허용하고 나머지는 모두 차단
- DDL, 대량 쓰기 (bulk writes) 및 민감 데이터 접근 시 인간의 승인 필요
- 모든 작업 및 승인 결정에 대한 불변의 감사 로그 (Immutable audit log)
- 프롬프트 주입 (prompt injection) 공격으로부터 보호하기 위해 에이전트의 컨텍스트 외부에서 강제 적용
처음 네 줄은 데이터베이스가 제 역할을 수행하는 것입니다. 나머지는 엔진이 할 수 없는 일을 제어 평면 (control plane)이 수행하는 것입니다. 당신에게는 이 두 가지가 모두 필요합니다.
출처
- OWASP Top 10 for LLM Applications: LLM01 Prompt Injection
- OWASP Top 10 for LLM Applications 2025 (PDF)
- OWASP Top 10 for Agentic Applications
- PostgreSQL Documentation: Predefined Roles
- PostgreSQL Documentation: Row Security Policies
- PostgreSQL Documentation: Client Connection Defaults (statement_timeout)
원문은 Datapace blog에 게시되었습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기