데이터베이스 MCP 서버 보안: 읽기 전용 계정, 쿼리 제한, 쓰기 분리
PostgreSQL 권한 설정과 도구 설계로 AI 에이전트의 DB 접근을 좁히는 방법
핵심 요약
- 데이터베이스 MCP 서버의 마지막 방어선은 쓰기 권한을 주지 않는 DB 역할 설계다.
- statement_timeout과 default_transaction_read_only는 보조 장치이며 세션에서 바뀔 수 있는 기본값이다.
- 자유 SQL 대신 매개변수화된 정해진 조회 도구를 제공한다.
- 쓰기 도구는 별도 서버와 별도 역할로 분리하고 호출마다 승인을 받는다.
AI 에이전트가 운영 데이터베이스를 직접 조회하게 하면 분석과 디버깅 속도가 빨라지지만, 잘못된 쿼리 하나가 테이블을 지우거나 DB 전체를 느리게 만들 수 있다. 프롬프트 인젝션으로 에이전트가 의도하지 않은 쿼리를 실행할 위험도 있다. MCP 명세는 서버가 모든 도구 입력을 검증하고, 접근 제어와 호출 빈도 제한을 구현하며, 출력을 정제해야 한다고 규정한다. 이 글은 PostgreSQL을 예로 데이터베이스용 MCP 서버의 방어선을 DB 권한, 도구 설계, 운영 통제 세 층으로 나눠 정리한다. 검색 단계 권한 필터링은 사내 문서 RAG 접근 제어 글에서 다뤘다.
방어선을 세 층으로 나누는 이유
| 층 | 수단 | 막는 위험 |
|---|---|---|
| DB 권한 | 읽기 전용 역할, 뷰만 허용, 타임아웃 | 도구 코드에 버그가 있어도 쓰기·과부하 차단 |
| 도구 설계 | 자유 SQL 대신 정해진 조회 도구, 입력 검증 | 의도하지 않은 테이블·컬럼 접근 |
| 운영 통제 | 쓰기 도구 분리·승인, 행 수 제한, 감사 로그 | 대량 유출, 사후 추적 불가 |
한 층만으로는 부족하다. 도구에서 SQL을 검사하더라도 우회 방법은 계속 나오므로, 마지막 방어선은 DB 계정 권한이어야 한다.
1단계: PostgreSQL 읽기 전용 역할 만들기
-- 에이전트 전용 로그인 역할
CREATE ROLE mcp_reader LOGIN PASSWORD '비밀번호는_비밀관리도구에서_주입';
-- 필요한 스키마와 뷰만 조회 허용(원본 테이블 대신 뷰)
GRANT USAGE ON SCHEMA reporting TO mcp_reader;
GRANT SELECT ON reporting.orders_summary, reporting.inventory_view TO mcp_reader;
-- 기본값: 읽기 전용 트랜잭션, 쿼리 5초 제한, 유휴 트랜잭션 30초 제한
ALTER ROLE mcp_reader SET default_transaction_read_only = on;
ALTER ROLE mcp_reader SET statement_timeout = '5s';
ALTER ROLE mcp_reader SET idle_in_transaction_session_timeout = '30s';PostgreSQL 문서에 따르면 statement_timeout은 지정 시간보다 오래 걸리는 문장을 중단하고, default_transaction_read_only는 새 트랜잭션의 기본 읽기 전용 여부를 정한다. 다만 이 값은 세션에서 SET으로 바꿀 수 있는 기본값일 뿐이므로, 실제 보호는 쓰기 권한을 아예 주지 않는 GRANT 설계가 담당한다. 개인정보 컬럼은 뷰에서 빼거나 마스킹해 노출 범위를 줄인다.
2단계: 자유 SQL 대신 정해진 조회 도구 제공하기
"SQL을 받아 실행하는 도구" 하나는 만들기 쉽지만 공격 면이 가장 넓다. 업무에 필요한 조회를 도구 단위로 정의하고, 쿼리는 서버 코드에서 매개변수화한다.
// 정해진 조회 도구의 예(Node.js, node-postgres)
server.registerTool(
'orders.daily_summary',
{
description: '특정 날짜의 주문 건수와 매출 합계를 조회한다. 날짜는 YYYY-MM-DD, 최근 90일만 허용.',
inputSchema: z.object({ date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/) }),
annotations: { readOnlyHint: true, openWorldHint: false },
},
async ({ date }) => {
const { rows } = await pool.query(
'SELECT order_count, revenue FROM reporting.orders_summary WHERE day = $1',
[date]
);
return { content: [{ type: 'text', text: JSON.stringify(rows[0] ?? null) }] };
}
);탐색형 질의가 꼭 필요해 SQL 입력을 허용해야 한다면 최소한 다음을 함께 적용한다.
- 읽기 전용 역할과 별도 커넥션 풀을 쓴다.
- 결과 행 수 상한(예:
LIMIT 200)을 서버가 강제로 덧붙이거나 결과를 잘라 반환한다. - 접근 가능한 스키마를 뷰 전용 스키마로 제한한다.
- 쿼리 원문과 호출자, 소요 시간을 감사 로그에 남긴다.
3단계: 쓰기 도구는 다른 서버로 분리하기
데이터 수정이 필요한 업무가 있다면 읽기 서버와 같은 프로세스에 넣지 말고, 별도 MCP 서버와 별도 DB 역할로 분리한다. 이렇게 하면 클라이언트에서 쓰기 서버만 꺼 두거나, 쓰기 서버에만 승인 절차를 강하게 걸 수 있다.
| 구분 | 읽기 서버 | 쓰기 서버 |
|---|---|---|
| DB 역할 | SELECT만, 뷰 중심 | 특정 테이블 INSERT/UPDATE만 |
| 도구 주석 | readOnlyHint: true | destructiveHint: true |
| 클라이언트 설정 | 상시 연결 | 필요할 때만 활성화, 호출마다 승인 |
| 입력 요건 | 조회 조건 | 멱등성 키, 변경 사유 |
명세는 클라이언트가 민감한 작업 전에 사용자 확인을 받고, 도구 입력을 호출 전에 사용자에게 보여 주라고 권한다. 쓰기 서버를 분리해 두면 이 확인 절차를 쓰기 작업에만 집중시킬 수 있다.
4단계: 운영 중 점검 항목
- 에이전트 역할로
INSERT,DROP을 직접 시도해 권한 오류가 나는지 확인한다. - 오래 걸리는 쿼리(
SELECT pg_sleep(10))로statement_timeout이 동작하는지 확인한다. - 도구 결과에 개인정보 컬럼이 포함되지 않는지 샘플을 점검한다.
- 감사 로그에서 호출자별 호출 수를 주기적으로 보고 비정상적인 대량 조회를 찾는다.
- DB 접속 정보는 설정 파일이 아니라 비밀 관리 도구나 환경 변수로 주입한다.
원격 서버로 운영할 때의 인가와 토큰 처리 원칙은 MCP 서버 OAuth 글을, 사내 도구의 개인정보 점검 항목은 바이브 코딩 사내 도구 보안 글을 참고한다.
초보자가 자주 실수하는 포인트
- 애플리케이션용 관리자 계정을 MCP 서버에 그대로 쓰는 경우
- default_transaction_read_only만 믿고 쓰기 권한을 회수하지 않는 경우
- 결과 행 수 제한 없이 대형 테이블 전체를 반환하는 경우
- 읽기·쓰기 도구를 한 서버에 두어 쓰기 도구만 끌 수 없는 경우
체크리스트
- 에이전트 전용 DB 역할에 SELECT 권한만 있는가
- statement_timeout과 유휴 트랜잭션 제한이 설정됐는가
- 조회 도구가 매개변수화된 쿼리를 쓰는가
- 쓰기 도구가 별도 서버로 분리되고 승인 절차가 있는가
- 쿼리와 호출자를 감사 로그로 남기는가
자주 묻는 질문
읽기 전용 복제본(replica)을 쓰면 충분한가요?
쓰기 차단에는 효과가 있지만, 개인정보 노출과 과도한 조회 부하는 막지 못한다. 뷰 제한과 행 수 제한을 함께 적용한다.
SQL 문자열 검사로 DELETE를 막으면 되지 않나요?
문자열 검사는 우회 방법이 많다. 검사는 보조 수단으로 두고, 권한 자체를 주지 않는 것이 기본이다.
에이전트가 스키마를 알아야 쿼리를 짤 수 있지 않나요?
테이블 정의는 MCP 리소스로 노출하고, 조회 자체는 정해진 도구로 하게 하면 맥락은 주면서 실행 범위는 좁힐 수 있다.
참고 자료 · 검증 기준
- PostgreSQL Docs — Client Connection Defaults
- MCP Specification 2026-07-28 — Tools (Security Considerations)
- MCP Specification 2026-07-28 — Security Best Practices
위 자료와 내용을 대조한 날짜: . 도구·서비스 정책은 이후 바뀔 수 있으므로 적용 전 공식 문서를 다시 확인하세요.
이 글은 위 참고 자료를 바탕으로 정리했으며, 내용은 운영 과정에서 순차적으로 보완될 수 있습니다.