← Cloudflare One / cloudflare-one / tutorials
Detekce provozu MCP v protokolech Gateway
Organizacím může chybět přehled o provozu Model Context Protocol (MCP), což zaměstnancům umožňuje připojovat se ke vzdáleným serverům MCP mimo dohled IT. Taková připojení s sebou nesou riziko úniku citlivých interních dat a přihlašovacích údajů, útoků typu tool injection nebo rizik spojených s dodavatelským řetězcem softwaru.
Jako správce IT chcete odhalit stínový provoz MCP, abyste zabránili neoprávněné exfiltraci dat, a zároveň nadále podporovali řízené případy použití. V tomto návodu použijete Cloudflare GraphQL Analytics API k prohledání protokolů Gateway HTTP a vyhledání vzorů provozu MCP, vytvoříte profily DLP, které detekují metody MCP JSON-RPC, a provoz rozdělíte tak, abyste odlišili autorizovaný provoz směřující na portály serverů MCP od provozu směřujícího na „stínové" vzdálené servery MCP.
Předpoklady
- Účet Cloudflare s Organizace Zero Trust
- Gateway se zapnutým filtrováním HTTP a aktivním přeposíláním provozu uživatelů přes proxy
- API token s následujícími oprávněními:
- Na úrovni účtu
Zero Trust: Read - Na úrovni účtu
DLP: Write - Na úrovni účtu
Gateway: Write
- Na úrovni účtu
- ID vašeho účtu Cloudflare (dostupné v Cloudflare dashboard ↗ v části Domovská stránka účtu)
- Znalost GraphQL Analytics API dotazy
- Praktická znalost TypeScript a REST API
1. Zkontrolujte Gateway HTTP dataset
gatewayHttpRequestsAdaptiveGroups dataset v GraphQL Analytics API poskytuje agregovaná data protokolů HTTP Gateway. Pomocí tohoto datasetu můžete dotazovat vzory provozu související s MCP:
- Dimenze:
httpHost,httpRequestURI,action,users,dlpProfiles - Časové rozmezí: Až 30 dní historických dat
- Seskupování: Agreguje výsledky podle hodnot dimenzí
- Filtrování: Podporuje
OR,AND, alikeoperátory
2. Vytvořte detekční dotaz MCP
Provoz MCP lze identifikovat pomocí tří signálů:
- Vzory domén: Hostname obsahující
mcp(napříkladmcp.datadog.com) - cesty URL: Standardní endpointy MCP, například
/mcp,/mcp/sse, a/sse - Shody DLP: Metody JSON-RPC v tělech požadavků (popsáno v dalším kroku)
Následující dotaz GraphQL prohledává protokoly Gateway a hledá první dva signály:
const query = `
query MCPTrafficScan($accountTag: string, $since: string, $until: string) {
viewer {
accounts(filter: { accountTag: $accountTag }) {
gatewayHttpRequestsAdaptiveGroups(
filter: {
datetime_geq: $since
datetime_leq: $until
OR: [
{ httpHost_like: "%mcp%" }
{ httpRequestURI_like: "%/mcp%" }
{ httpRequestURI_like: "%/sse%" }
]
}
limit: 10000
) {
dimensions {
httpHost
action
users
}
count
}
}
}
}
`;
const variables = {
accountTag: "<YOUR_ACCOUNT_ID>",
since: "<START_DATE>", // ISO-8601 format, for example 2025-03-08T00:00:00Z
until: "<END_DATE>", // Up to 30 days after start date
};
const response = await fetch("https://api.cloudflare.com/client/v4/graphql", {
method: "POST",
headers: {
Authorization: `Bearer ${apiToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ query, variables }),
});
const data = await response.json();
const groups =
data.data?.viewer?.accounts?.[0]?.gatewayHttpRequestsAdaptiveGroups || [];const query = `
query MCPTrafficScan($accountTag: string, $since: string, $until: string) {
viewer {
accounts(filter: { accountTag: $accountTag }) {
gatewayHttpRequestsAdaptiveGroups(
filter: {
datetime_geq: $since
datetime_leq: $until
OR: [
{ httpHost_like: "%mcp%" }
{ httpRequestURI_like: "%/mcp%" }
{ httpRequestURI_like: "%/sse%" }
]
}
limit: 10000
) {
dimensions {
httpHost
action
users
}
count
}
}
}
}
`;
const variables = {
accountTag: "<YOUR_ACCOUNT_ID>",
since: "<START_DATE>", // ISO-8601 format, for example 2025-03-08T00:00:00Z
until: "<END_DATE>", // Up to 30 days after start date
};
const response = await fetch("https://api.cloudflare.com/client/v4/graphql", {
method: "POST",
headers: {
Authorization: `Bearer ${apiToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ query, variables }),
});
const data = await response.json();
const groups =
data.data?.viewer?.accounts?.[0]?.gatewayHttpRequestsAdaptiveGroups || [];Nahraďte <YOUR_ACCOUNT_ID> s ID vašeho účtu Cloudflare. Nahraďte <START_DATE> a <END_DATE> s časovými značkami ve formátu ISO-8601 pokrývajícími požadovaný časový rozsah (až 30 dnů).
3. Zpracujte výsledky dotazu
Každá skupina v odpovědi představuje agregovaný provoz pro konkrétní httpHost a action kombinace. Výsledky rozeberte a zjistěte, která připojení MCP nejsou blokována:
const hits = groups.map((group) => ({
domain: group.dimensions.httpHost,
requestCount: group.count,
users: group.dimensions.users || [],
actions: {
allowed: group.dimensions.action === "allow" ? group.count : 0,
blocked: group.dimensions.action === "block" ? group.count : 0,
},
}));
const totalMCPRequests = hits.reduce((sum, h) => sum + h.requestCount, 0);
const unblockedHits = hits.filter((h) => h.actions.allowed > 0);
console.log(`Found ${totalMCPRequests} MCP requests`);
console.log(`${unblockedHits.length} destinations are unblocked`);interface MCPTrafficHit {
domain: string;
requestCount: number;
users: string[];
actions: {
allowed: number;
blocked: number;
};
}
const hits: MCPTrafficHit[] = groups.map((group: any) => ({
domain: group.dimensions.httpHost,
requestCount: group.count,
users: group.dimensions.users || [],
actions: {
allowed: group.dimensions.action === "allow" ? group.count : 0,
blocked: group.dimensions.action === "block" ? group.count : 0,
},
}));
const totalMCPRequests = hits.reduce((sum, h) => sum + h.requestCount, 0);
const unblockedHits = hits.filter((h) => h.actions.allowed > 0);
console.log(`Found ${totalMCPRequests} MCP requests`);
console.log(`${unblockedHits.length} destinations are unblocked`);Klíčové poznatky z dat:
- Neblokovaný provoz (
action=allow) - aktivní připojení MCP, která je potřeba prošetřit nebo zablokovat - Blokovaný provoz (
action=block) - vaše stávající zásady fungují - Přiřazení uživateli: Toto udává, kteří zaměstnanci se připojují k serverům MCP
4. Vytvořte profily DLP pro detekci MCP JSON-RPC
Zásady Gateway HTTP mohou porovnávat domény a cesty URL, ale nemohou kontrolovat těla požadavků. Profily DLP skenují POST obsah těla podle vzorů, což je užitečné pro detekci shadow MCP, protože MCP používá JSON-RPC přes HTTP a má několik detekovatelných charakteristických znaků.
Každý požadavek MCP obsahuje "method" pole:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": { "name": "read_file", "arguments": { "path": "/etc/passwd" } }
}Útočník by mohl spustit server MCP na nestandardní doméně (například internal-tools.company.com/api/assistant) aniž by se aktivovala pravidla založená na doméně nebo cestě. Můžete použít kontroly DLP POST tělo pro "method": "tools/call" a dalších vzorů specifických pro MCP pro zajištění robustnější ochrany provozu MCP.
Zkontrolujte omezení DLP
Před vytvářením detekčních vzorů si všimněte následujících omezení DLP:
- Syntaxe regulárních výrazů : Regulární výrazy Rust (mírně se liší od JavaScriptu a PCRE)
- Hloubka kontroly : Pouze prvních 1,024 bajtů těla požadavku
- Pouze POST : DLP skenuje pouze
POSTpožadavky - Výkon : Vzory regulárních výrazů musí být efektivní, aby se předešlo katastrofickému backtrackingu
Sestavit detekční vzory MCP
Indikátory MCP lze nalézt v polích metod protokolu JSON-RPC. Následující regulární výrazy pokrývají základní metody protokolu MCP:
const DLP_REGEX_PATTERNS = [
{
name: "MCP Initialize Method",
regex: '"method"\\s{0,5}:\\s{0,5}"initialize"',
},
{
name: "MCP Tools Call",
regex: '"method"\\s{0,5}:\\s{0,5}"tools/call"',
},
{
name: "MCP Tools List",
regex: '"method"\\s{0,5}:\\s{0,5}"tools/list"',
},
{
name: "MCP Resources Read",
regex: '"method"\\s{0,5}:\\s{0,5}"resources/read"',
},
{
name: "MCP Resources List",
regex: '"method"\\s{0,5}:\\s{0,5}"resources/list"',
},
{
name: "MCP Prompts List",
regex: '"method"\\s{0,5}:\\s{0,5}"prompts/(list|get)"',
},
{
name: "MCP Sampling Create Message",
regex: '"method"\\s{0,5}:\\s{0,5}"sampling/createMessage"',
},
{
name: "MCP Protocol Version",
regex: '"protocolVersion"\\s{0,5}:\\s{0,5}"202[4-9]',
},
{
name: "MCP Notifications Initialized",
regex: '"method"\\s{0,5}:\\s{0,5}"notifications/initialized"',
},
{
name: "MCP Roots List",
regex: '"method"\\s{0,5}:\\s{0,5}"roots/list"',
},
];const DLP_REGEX_PATTERNS = [
{
name: "MCP Initialize Method",
regex: '"method"\\s{0,5}:\\s{0,5}"initialize"',
},
{
name: "MCP Tools Call",
regex: '"method"\\s{0,5}:\\s{0,5}"tools/call"',
},
{
name: "MCP Tools List",
regex: '"method"\\s{0,5}:\\s{0,5}"tools/list"',
},
{
name: "MCP Resources Read",
regex: '"method"\\s{0,5}:\\s{0,5}"resources/read"',
},
{
name: "MCP Resources List",
regex: '"method"\\s{0,5}:\\s{0,5}"resources/list"',
},
{
name: "MCP Prompts List",
regex: '"method"\\s{0,5}:\\s{0,5}"prompts/(list|get)"',
},
{
name: "MCP Sampling Create Message",
regex: '"method"\\s{0,5}:\\s{0,5}"sampling/createMessage"',
},
{
name: "MCP Protocol Version",
regex: '"protocolVersion"\\s{0,5}:\\s{0,5}"202[4-9]',
},
{
name: "MCP Notifications Initialized",
regex: '"method"\\s{0,5}:\\s{0,5}"notifications/initialized"',
},
{
name: "MCP Roots List",
regex: '"method"\\s{0,5}:\\s{0,5}"roots/list"',
},
];Vysvětlení vzoru:
\\s{0,5}: Umožňuje nula až pět bílých znaků, aby fungovalo jak s minifikovaným, tak s formátovaným JSON"method": Dvojité uvozovky jsou doslovné, protože je vyžaduje JSON"tools/call": Odpovídá přesnému názvu metody MCP202[4-9]: Odpovídá verzím protokolu MCP 2024 až 2029
Vytvořit profil DLP přes API
Odešlete POST požadavek k vytvoření vlastního profilu DLP obsahujícího všechny vzory detekce:
const dlpProfile = {
name: "MCP-Shield: MCP JSON-RPC Detection",
description: "Detects MCP protocol JSON-RPC methods in HTTP request bodies.",
type: "custom",
entries: DLP_REGEX_PATTERNS.map((p) => ({
name: p.name,
enabled: true,
pattern: {
regex: p.regex,
validation: "luhn",
},
})),
};
const response = await fetch(
`https://api.cloudflare.com/client/v4/accounts/${accountId}/gateway/rules`,
{
method: "POST",
headers: {
Authorization: `Bearer ${apiToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify(dlpRule),
},
);
const data = await response.json();
if (data.success) {
console.log(`Created DLP profile: ${data.result.id}`);
}const dlpProfile = {
name: "MCP-Shield: MCP JSON-RPC Detection",
description: "Detects MCP protocol JSON-RPC methods in HTTP request bodies.",
type: "custom",
entries: DLP_REGEX_PATTERNS.map((p) => ({
name: p.name,
enabled: true,
pattern: {
regex: p.regex,
validation: "luhn",
},
})),
};
const response = await fetch(
`https://api.cloudflare.com/client/v4/accounts/${accountId}/gateway/rules`,
{
method: "POST",
headers: {
Authorization: `Bearer ${apiToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify(dlpRule),
},
);
const data = await response.json();
if (data.success) {
console.log(`Created DLP profile: ${data.result.id}`);
}Nahraďte ${accountId} s ID vašeho účtu Cloudflare a ${apiToken} s vaším API tokenem.
Odkažte na profil DLP v pravidle Gateway
Jakmile profil DLP existuje, vytvořte zásadu Gateway HTTP, která bude blokovat požadavky odpovídající tomuto profilu:
const dlpRule = {
name: "MCP-Shield: Block MCP JSON-RPC via DLP",
description: "Blocks requests with MCP JSON-RPC patterns detected by DLP",
precedence: 85,
enabled: true,
action: "block",
filters: ["http"],
traffic:
'any(http.request.body.scan.dlp.profiles[*] == "MCP-Shield: MCP JSON-RPC Detection")',
};const dlpRule = {
name: "MCP-Shield: Block MCP JSON-RPC via DLP",
description: "Blocks requests with MCP JSON-RPC patterns detected by DLP",
precedence: 85,
enabled: true,
action: "block",
filters: ["http"],
traffic:
'any(http.request.body.scan.dlp.profiles[*] == "MCP-Shield: MCP JSON-RPC Detection")',
};Toto pravidlo se spustí, když profil DLP odpovídá některému z regulárních výrazů (regex) v těle požadavku.
5. Klasifikujte provoz Portal a provoz shadow MCP
Cloudflare MCP Server Portals poskytují řízenou infrastrukturu pro schválený přístup MCP v rámci vaší organizace, včetně:
- Řízený přístup : Centralizovaná infrastruktura MCP spravovaná vaším IT týmem
- Auditní stopy : Všechny požadavky MCP zaznamenané prostřednictvím Gateway s přiřazením uživatele
- Vynucování zásad : Zásady Zero Trust se uplatňují automaticky, včetně ověřování a DLP
- Schválené nástroje : Kurátorská sada nástrojů a prostředků MCP prověřená bezpečnostním týmem
Při analýze protokolů Gateway je užitečné rozlišovat dva typy provozu MCP:
| Typ provozu | Vlastnosti | Úroveň rizika | Akce |
|---|---|---|---|
| Provoz MCP Portal | httpHost odpovídá doméně vašeho portálu (například mcp.yourcompany.com nebo mcp-portal.pages.dev) |
Autorizováno | Sledovat |
| Shadow MCP traffic | httpHost neodpovídá žádné doméně portálu (například mcp.datadog.com, api.stripe.com/mcp) |
Prošetření | Blokovat, přesměrovat nebo zkontrolovat |
Prodlužte zpracování dotazu z Zpracujte výsledky dotazu klasifikovat provoz porovnáním hostname se seznamem schválených domén portálu:
const portalDomains = [
"mcp.yourcompany.com",
"mcp-portal.pages.dev",
"approved-mcp.workers.dev",
];
const results = groups.map((group) => {
const isPortalTraffic = portalDomains.some((domain) =>
group.dimensions.httpHost.includes(domain),
);
return {
domain: group.dimensions.httpHost,
requestCount: group.count,
users: group.dimensions.users || [],
trafficType: isPortalTraffic ? "portal" : "shadow",
riskLevel: isPortalTraffic ? "low" : "high",
};
});
const portalTraffic = results.filter((r) => r.trafficType === "portal");
const shadowTraffic = results.filter((r) => r.trafficType === "shadow");
console.log("Portal traffic:", portalTraffic);
console.log("Shadow MCP traffic:", shadowTraffic);const portalDomains = [
"mcp.yourcompany.com",
"mcp-portal.pages.dev",
"approved-mcp.workers.dev",
];
const results = groups.map((group) => {
const isPortalTraffic = portalDomains.some((domain) =>
group.dimensions.httpHost.includes(domain),
);
return {
domain: group.dimensions.httpHost,
requestCount: group.count,
users: group.dimensions.users || [],
trafficType: isPortalTraffic ? "portal" : "shadow",
riskLevel: isPortalTraffic ? "low" : "high",
};
});
const portalTraffic = results.filter((r) => r.trafficType === "portal");
const shadowTraffic = results.filter((r) => r.trafficType === "shadow");
console.log("Portal traffic:", portalTraffic);
console.log("Shadow MCP traffic:", shadowTraffic);Nahraďte portalDomains array se skutečnými doménami vašich schválených MCP Server Portals.