Skip to main content
Glama
bhargavlukka

SecureAgentServer

by bhargavlukka

보안 MCP 기반 에이전트 시스템

지원 티켓 + 고객 계정 MCP 서버 및 클라이언트를 종단 간 보안을 갖추어 구축했습니다. 서명된 토큰 인증, 프롬프트 인젝션과 도구 중독에 대한 두 개의 독립적인 가드레일, 문서화된 위협 모델, 그리고 하나뿐인 파괴적 도구 호출에 대한 인간 개입(HITL) 게이트까지 포함합니다. "Secure an MCP-Based Agent System" 랩을 위해 제작되었습니다.

아키텍처

flowchart TD
    HOST["Host application"] --> CLIENT["client.py\n(fastmcp.Client + elicitation_handler)"]
    CLIENT <-->|"Streamable HTTP\nAuthorization: Bearer <signed JWT>"| SERVER

    subgraph Server["server.py — FastMCP('SecureAgentServer')"]
        AUTH["JWTVerifier (HS256)\nissuer + audience + signature checked"]
        G1["Guardrail 1: sanitize_untrusted_text()\napplied to ticket body/subject"]
        G2["Guardrail 2: verify_tool_manifest()\nchecked at startup, refuses to start on mismatch"]
        TOOLS["Tools: search_tickets, lookup_customer_account,\nclose_ticket (elicitation-gated)"]
        AUTH --> TOOLS
        TOOLS --> G1
    end
    G2 -.->|startup check| SERVER

    TOOLS --> TICKETS[("data/tickets.json\n(untrusted customer text)")]
    TOOLS --> CUSTOMERS[("data/customers.json\n('internal API')")]

Related MCP server: enterprise-agent-lab

설정

pip install -r requirements.txt

# 1. Set the JWT signing secret (never commit the real value; see .env.example)
export MCP_JWT_SECRET="a-long-random-secret-at-least-32-characters"

# 2. Generate the pinned tool-integrity manifest (a deliberate, manual step —
#    see docs/threat-model.md Risk #2)
python generate_manifest.py

# 3. Run the server
python server.py

# 4. In another terminal (same MCP_JWT_SECRET exported)
python client.py --auto-confirm   # non-interactive demo
python client.py                  # interactive: real yes/no confirmation prompts

보안 통제 재현

제어

검증 방법

Signed-JWT 인증

client.py의 마지막 두 데모: 읽기 전용 범위의 토큰은 close_ticket에서 거부되고, 위조되었거나 서명되지 않은 토큰은 어떤 도구도 실행되기 전에 401로 거부됩니다. 두 경우 모두 demo/session_log.txt에 기록됩니다.

가드레일 1: 프롬프트 인젝션 정화

클라이언트를 실행하고 search_tickets의 출력을 확인하며 TICKET-2002를 찾으세요(본문에 심어둔 "IGNORE ALL PREVIOUS INSTRUCTIONS..." 페이로드가 들어 있음). 반환된 텍스트는 감싸지고 트리거 문구는 삭제됩니다. 이 가드레일이 작동하면 서버 자체 stdout에 [SECURITY] 줄이 기록됩니다.

가드레일 2: 도구 중독 탐지

python demo/verify_tampering_detection.py — close_ticket의 설명을 메모리에서 변조한 뒤 verify_tool_manifest()가 불일치를 탐지하는 과정을 보여줍니다. 캡처된 실행 결과는 demo/tampering_detection_log.txt를 참조하세요.

파괴적 작업에 대한 인간 개입(HITL)

데모 로그에서 close_ticket은 [ELICITATION] ... -> accepting 이후에만 완료됩니다. --auto-confirm 없이 client.py를 실행하면 실제 대화형 확인 프롬프트를 확인할 수 있습니다.

최소 권한 범위 지정

add_ticket_note/close_ticket는 연결 수준만 요구하는 것이 아니라 get_access_token()(server.py::_require_scope)이용해 user write:tickets`를 명시적으로 검사합니다. 읽기 전용 토큰이 거부되는 것으로 입증됩니다.

위협 모델

완전한 문서에는 완화 조치와 남은 위험 메모가 애매모호하게 넘어가기 않도록 명시된 5가지 위험이 포함되어 있습니다: docs/threat-model.md

의도적으로 범위 밖에 둔 것, 명확하게 밝힘

  • mint_token.py는 실제 OAuth 2.1 식별 공급자 대신 임시로 사용되는 대체물입니다. 프로덕션 배포에는 로컬 발행 스크립트가 아니라 실제 토큰 발급/회전/폐기가 필요합니다.

  • 비율 제한이나 네트워크 계층 강화(TLS 종료, WAF)는 없습니다. 이 프로젝트는 응용 계층 보안 데모이지 전체 배포 강화 가이드가 아닙니다.

  • 가드레일 1의 정규식 기반 부분은 명시적으로 보조적이고 가능한 최선의 계층입니다. 실제로 의존하는 통제가 구분자 래핑인 이유는 docs/threat-model.md의 위험 #1을 참조하세요.

커밋된 비밀 정보 없음

MCP_JWT_SECRET은 환경에서 읽으며(.env.example 참고) 하드코딩되거나 커밋되지 않습니다. template_manifest.json은 의도적으로 커밋됩니다. 감사용 해시 매니페스트(lockfile처럼)이지 비밀 정보가 아닙니다.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    C
    maintenance
    Enables controlled AI-agent access to enterprise-shaped tools with a deny-by-default gated write path, human approval, dry-run execution, and append-only audit logging.
    1
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enforces authenticated identity on every tool call and SSE frame, rotates vaulted credentials in place, and restricts tools via allowlists.
    71 npm
    MIT