이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- MCP server에서 인증과 인가는 어디서 강제해야 하는가?
- tool 호출 audit log에는 무엇을 남겨야 하는가?
- read-only tool에도 감사 로그가 필요한 이유는 무엇인가?
- 보안팀과 어떤 위험을 합의해야 하는가?
개요
MCP server는 AI 애플리케이션이 외부 시스템에 접근하는 문이다. 이 문이 사내 문서, 고객 데이터, 업무 API에 연결되면 일반 API 서버와 같은 수준의 보안 설계가 필요하다.
프롬프트에 “권한 없는 작업을 하지 마라”고 적는 것은 보조 장치일 뿐이다. 권한은 서버에서 강제해야 한다.
인증과 인가
인증은 호출 주체를 확인하는 것이다. 인가는 그 주체가 특정 tool이나 resource를 사용할 수 있는지 확인하는 것이다.
MCP server에서 주의할 점은 모델이 사용자를 대신해 tool을 호출한다는 점이다. 실제 권한 판단은 다음 정보를 기준으로 해야 한다.
- 사용자 id
- tenant id
- role
- scope
- tool name
- resource id
- 요청 목적
host가 넘긴 문자열을 그대로 믿지 않는다. 가능하면 서명된 토큰, 세션, mTLS, 내부 gateway 인증처럼 검증 가능한 방식으로 actor를 확인한다.
감사 로그
tool 호출 audit log에는 다음이 필요하다.
{
"event": "mcp.tool.called",
"actor_id": "user-42",
"tenant_id": "tenant-a",
"tool_name": "create_support_ticket",
"tool_version": "v1",
"input_hash": "sha256:...",
"input_summary": "priority=high,title_length=24",
"status": "created",
"resource_id": "ticket-991",
"idempotency_key": "idem-123",
"trace_id": "trace-abc",
"elapsed_ms": 340
}민감한 input 전문은 기본 로그에 남기지 않는다. 필요하다면 별도 보안 저장소에 제한적으로 저장한다.
Read-only Tool도 위험하다
read-only tool은 시스템 상태를 바꾸지 않지만 정보 유출 위험이 있다.
- 고객 개인정보 조회
- 내부 문서 조회
- 장애 상태 조회
- 비용/매출 데이터 조회
- 보안 설정 조회
따라서 read-only tool도 actor, resource, 조회 범위, 결과 개수, trace id를 남겨야 한다.
코드로 이해하기
다음은 tool 실행 전 권한을 확인하고 audit log를 남기는 wrapper 예시다.
type ToolHandler<I, O> = (input: I, context: ToolContext) => Promise<O>;
type ToolContext = {
actorId: string;
tenantId: string;
traceId: string;
};
function withAuthorization<I, O>(
toolName: string,
requiredScope: string,
handler: ToolHandler<I, O>,
): ToolHandler<I, O> {
return async (input, context) => {
const started = Date.now();
await authorizationService.requireScope({
actorId: context.actorId,
tenantId: context.tenantId,
scope: requiredScope,
});
try {
const output = await handler(input, context);
auditLogger.info({
event: "mcp.tool.success",
toolName,
actorId: context.actorId,
tenantId: context.tenantId,
traceId: context.traceId,
elapsedMs: Date.now() - started,
});
return output;
} catch (error) {
auditLogger.warn({
event: "mcp.tool.failed",
toolName,
actorId: context.actorId,
tenantId: context.tenantId,
traceId: context.traceId,
errorType: error.constructor.name,
});
throw error;
}
};
}핵심은 tool handler 내부에서 권한을 흩어 처리하지 않고 공통 wrapper나 middleware로 강제하는 것이다.
장애 상황과 대응
보안 장애는 다음처럼 발생할 수 있다.
- host가 잘못된 tenant id를 넘겨 다른 tenant 데이터가 조회된다.
- tool result가 로그에 평문으로 남아 개인정보가 유출된다.
- read-only tool 조회량이 비정상적으로 증가한다.
- prompt injection으로 모델이 권한 없는 tool 호출을 시도한다.
- write tool이 승인 없이 실행된다.
대응은 서버 권한 검사, audit log, anomaly detection, tool별 scope 제한, 민감정보 마스킹이다.
인프라 협업 포인트
인프라팀과 보안팀이 보는 포인트는 다르지만 연결되어 있다.
- MCP server가 어느 네트워크에 배포되는가?
- 어떤 secret을 갖고 있는가?
- 어떤 내부 API에 접근할 수 있는가?
- 로그는 어디로 가고 누가 볼 수 있는가?
- tool별 rate limit은 있는가?
- 퇴사자나 권한 변경 시 access가 즉시 반영되는가?
MCP server가 내부망에서 강한 권한을 갖고 있으면 작은 취약점도 큰 사고가 된다.
개인 프로젝트 최소 기준
- API key를 코드에 넣지 않는다.
- tool 호출 로그를 남긴다.
- 민감한 원문은 로그에 남기지 않는다.
- write tool은 실행 전 confirmation을 둔다.
기업 운영 수준 기준
- 중앙 인증과 scope 기반 인가를 적용한다.
- read/write tool 권한을 분리한다.
- audit log를 변조 어렵게 보관한다.
- 민감정보 마스킹과 보존 기간을 둔다.
- 보안팀과 threat model을 작성한다.
실전 팁
- 권한 없는 요청은 모델에게 넘기기 전에 서버에서 차단한다.
- tool 호출 결과에는 필요한 최소 정보만 반환한다.
- audit log에는 input 전문보다 input summary와 hash를 우선 남긴다.
- write tool은 actor와 approval actor를 분리할 수 있게 설계한다.
위험 신호!
- tenant id를 모델이 생성하거나 사용자가 넘긴 값을 그대로 믿는다.
- tool 호출 로그가 없다.
- read-only tool은 감사하지 않는다.
- tool result 전문이 일반 로그에 남는다.
- MCP server가 과도한 내부 API 권한을 가진다.
확인 질문
확인 질문
- MCP server에서 권한은 어디서 강제해야 하는가?
- 프롬프트가 아니라 MCP server와 백엔드 인증/인가 계층에서 강제해야 한다.
- read-only tool에도 audit log가 필요한 이유는 무엇인가?
- 정보 조회 자체가 개인정보나 내부 정보 유출 사고가 될 수 있기 때문이다.
- audit log에 input 전문을 무조건 남기면 안 되는 이유는 무엇인가?
- 개인정보와 민감 데이터가 로그 시스템에 노출될 수 있기 때문이다.
Spring AI 적용 연결
- Spring AI MCP 통합에서는 tool 호출 주체, 권한 scope, 감사 로그를 서버 경계에서 남겨야 한다. 보안·운영 경계는 08. MCP Client Server 통합에서 이어진다.
- 운영 배포 전 점검 항목은 12. Production Readiness와 인프라 협업을 함께 본다.