スキル一覧に戻る
yonatangross

mcp-security-hardening

by yonatangross

The Complete AI Development Toolkit for Claude Code — 159 skills, 34 agents, 20 commands, 144 hooks. Production-ready patterns for FastAPI, React 19, LangGraph, security, and testing.

29🍴 4📅 2026年1月23日
GitHubで見るManusで実行

SKILL.md


name: mcp-security-hardening description: MCP security patterns for prompt injection defense, tool poisoning prevention, and permission management. Use when securing MCP servers, validating tool descriptions, implementing allowlists. version: 1.0.0 tags: [mcp, security, prompt-injection, tool-poisoning, allowlist, zero-trust, 2026] context: fork agent: security-auditor author: OrchestKit user-invocable: false

MCP Security Hardening

Defense-in-depth security patterns for Model Context Protocol (MCP) integrations.

Overview

  • Securing MCP server implementations
  • Validating tool descriptions before LLM exposure
  • Implementing zero-trust tool allowlists
  • Detecting tool poisoning attacks (TPA)
  • Managing tool permissions and capabilities

Core Security Principle

Treat ALL tool descriptions as untrusted input. Validate tool identity with hash verification. Apply least privilege to all tool capabilities.

Threat Model Summary

Attack VectorDefenseImplementation
Tool Poisoning (TPA)Zero-trust allowlistHash verification, mandatory vetting
Prompt InjectionDescription sanitizationRegex filtering, encoding detection
Rug PullChange detectionHash comparison on each invocation
Data ExfiltrationOutput filteringSensitive pattern removal
Session HijackingSecure sessionsCryptographic IDs, no auth in sessions

Layer 1: Tool Description Sanitization

import re

FORBIDDEN_PATTERNS = [
    r"ignore previous", r"system prompt", r"<.*instruction.*>",
    r"IMPORTANT:", r"override", r"admin", r"sudo",
    r"\\x[0-9a-fA-F]{2}",  # Hex encoding
    r"&#x?[0-9a-fA-F]+;",  # HTML entities
]

def sanitize_tool_description(description: str) -> str:
    """Remove instruction-like phrases or encoding tricks."""
    if not description:
        return ""
    sanitized = description
    for pattern in FORBIDDEN_PATTERNS:
        sanitized = re.sub(pattern, "[REDACTED]", sanitized, flags=re.I)
    return sanitized.strip()

def detect_injection_attempt(description: str) -> str | None:
    """Detect prompt injection patterns."""
    indicators = [
        (r"ignore.*previous", "instruction_override"),
        (r"you are now", "role_hijack"),
        (r"forget.*above", "context_wipe"),
    ]
    for pattern, attack_type in indicators:
        if re.search(pattern, description, re.I):
            return attack_type
    return None

Layer 2: Zero-Trust Tool Allowlist

from hashlib import sha256
from dataclasses import dataclass
from datetime import datetime, timezone

@dataclass
class AllowedTool:
    name: str
    description_hash: str
    capabilities: list[str]
    approved_at: datetime
    approved_by: str
    max_calls_per_minute: int = 60
    requires_human_approval: bool = False

class MCPToolAllowlist:
    """Zero-trust allowlist - every tool must be explicitly vetted."""

    def __init__(self):
        self._allowed_tools: dict[str, AllowedTool] = {}
        self._call_counts: dict[str, list[datetime]] = {}

    def register(self, tool: AllowedTool) -> None:
        self._allowed_tools[tool.name] = tool
        self._call_counts[tool.name] = []

    def compute_hash(self, description: str) -> str:
        return sha256(description.encode('utf-8')).hexdigest()

    def validate(self, tool_name: str, description: str) -> tuple[bool, str]:
        if tool_name not in self._allowed_tools:
            return False, f"Tool '{tool_name}' not in allowlist"

        expected = self._allowed_tools[tool_name]
        if self.compute_hash(description) != expected.description_hash:
            return False, "Tool description changed (possible rug pull)"

        # Rate limit check
        now = datetime.now(timezone.utc)
        recent = [t for t in self._call_counts[tool_name] if (now - t).total_seconds() < 60]
        if len(recent) >= expected.max_calls_per_minute:
            return False, "Rate limit exceeded"

        self._call_counts[tool_name] = recent + [now]
        return True, "Validated"

Layer 3: Capability Declarations

from enum import Enum

class ToolCapability(Enum):
    READ_FILE = "read:file"
    WRITE_FILE = "write:file"
    EXECUTE_COMMAND = "execute:command"
    NETWORK_REQUEST = "network:request"
    DATABASE_WRITE = "database:write"

class CapabilityEnforcer:
    SENSITIVE_PATHS = ["/etc/passwd", "~/.ssh", ".env", "credentials", "secrets"]

    def __init__(self):
        self._declarations: dict[str, set[ToolCapability]] = {}

    def register(self, tool_name: str, capabilities: set[ToolCapability]) -> None:
        self._declarations[tool_name] = capabilities

    def check(self, tool_name: str, capability: ToolCapability, resource: str = "") -> tuple[bool, str]:
        if tool_name not in self._declarations:
            return False, "No capability declaration found"

        if capability not in self._declarations[tool_name]:
            return False, f"Capability {capability.value} not allowed"

        if capability in (ToolCapability.READ_FILE, ToolCapability.WRITE_FILE):
            for sensitive in self.SENSITIVE_PATHS:
                if sensitive in resource:
                    return False, "Access to sensitive path denied"

        return True, "Allowed"

Layer 4: Session Security

import secrets
from datetime import datetime, timedelta, timezone
from dataclasses import dataclass

def generate_secure_session_id() -> str:
    return secrets.token_urlsafe(32)  # 256 bits of entropy

@dataclass
class MCPSession:
    session_id: str
    created_at: datetime
    last_activity: datetime
    request_count: int = 0
    max_requests_per_minute: int = 100
    timeout_minutes: int = 30

    def is_valid(self) -> tuple[bool, str]:
        now = datetime.now(timezone.utc)
        if (now - self.last_activity) > timedelta(minutes=self.timeout_minutes):
            return False, "Session timed out"
        return True, "Valid"

    def record_request(self) -> tuple[bool, str]:
        now = datetime.now(timezone.utc)
        if (now - self.last_activity).total_seconds() >= 60:
            self.request_count = 0
        self.request_count += 1
        self.last_activity = now
        if self.request_count > self.max_requests_per_minute:
            return False, "Rate limit exceeded"
        return True, "OK"

Anti-Patterns (FORBIDDEN)

# NEVER trust tool descriptions without sanitization
prompt = f"Use this tool: {tool.description}"  # INJECTION RISK!

# NEVER allow tools without explicit vetting
return mcp.list_tools()  # No validation!

# NEVER store auth tokens in session IDs
session_id = f"{user_id}:{auth_token}"  # CREDENTIAL LEAK!

# NEVER skip hash verification on tool calls

# ALWAYS sanitize, validate, and verify:
sanitized = sanitize_tool_description(tool.description)
is_valid, reason = allowlist.validate(tool.name, tool.description)
session_id = secrets.token_urlsafe(32)

Key Decisions

DecisionRecommendation
Tool trust modelZero-trust (explicit allowlist)
Description handlingSanitize + hash verify
Session IDsCryptographic (secrets.token_urlsafe)
Rate limitingPer-tool and per-session
Sensitive operationsHuman-in-the-loop approval
  • llm-safety-patterns - LLM-specific security patterns
  • input-validation - Input sanitization fundamentals
  • auth-patterns - Session and token security
  • defense-in-depth - Layered security architecture

スコア

総合スコア

75/100

リポジトリの品質指標に基づく評価

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

+10
説明文

100文字以上の説明がある

+10
人気

GitHub Stars 100以上

0/15
最近の活動

1ヶ月以内に更新

+10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

+5

レビュー

💬

レビュー機能は近日公開予定です