Naar de inhoud
Koen Holman
← Terug naar het onderzoek

Veiligheid & grenzen

Model Context Protocol: vertrouwen bindt aan de naam, niet aan de inhoud

10 min lezen

MCP verbindt AI-agents met tools van derden. Drie los ontdekte kwetsbaarheden delen hetzelfde patroon: vertrouwen bindt aan een naam, niet aan de inhoud.

Op 25 november 2024 maakte Anthropic het Model Context Protocol open source: een standaard waarmee een AI-agent verbindt met “de systemen waar de data leeft”, in plaats van voor elke combinatie van model en tool een eigen integratie te bouwen. Het idee is een stekker: één protocol, meerdere aansluitingen.

De architectuur is precies zo eenvoudig als dat klinkt. Een host — Claude Desktop, Claude Code, Visual Studio Code, Cursor — opent voor elke server waarmee hij verbindt een eigen client, en die client praat een vaste taal: hij vraagt welke tools de server aanbiedt (tools/list), en roept ze aan (tools/call). Wat een server aanbiedt kan veranderen — het protocol heeft een notificatie voor precies dat geval, notifications/tools/list_changed— en een client die daarop is geabonneerd vraagt de lijst dan opnieuw op.

Dat laatste zinnetje — de client vraagt de lijst gewoon opnieuw op — is onschuldiger dan het klinkt, en het is waar dit stuk over gaat.

Eén goedkeuring, oneindig veel aanroepen

In de praktijk wordt een MCP-server goedgekeurd op het moment dat een host ermee verbindt: de tool-beschrijvingen komen binnen, een gebruiker keurt ze goed, en vanaf dat moment kan het model ze aanroepen. Wat er daarna gebeurt staat niet beschreven als een herhaalde controle — het is een lijst die is opgehaald, niet een contract dat bij elke aanroep opnieuw wordt getekend.

Dat gat tussen “goedgekeurd bij het verbinden” en “vertrouwd bij elke aanroep daarna” is precies waar drie los van elkaar gepubliceerde kwetsbaarheden op uitkomen, elk op een andere plek in het protocol.

Hostbv. Claude DesktopClientéén per serverServervan een derdekeurt tools eenmalig goedTool poisoningverstopt in de beschrijvingdie het model wel leestRug pullvertrouwen bindt aande naam, niet de inhoudConfused deputytoestemming bindt aanéén gedeeld cliënt-ID
Conceptueel, geen protocolschema. Drie aanvalsvormen op drie verschillende plekken in de keten — de tool-beschrijving, de tijd tussen goedkeuring en gebruik, en de OAuth-laag van een proxy — delen hetzelfde patroon: wat gecontroleerd wordt bij het goedkeuren, is niet noodzakelijk wat er bij de volgende aanroep binnenkomt.

Drie plekken waar het vertrouwen breekt, met hetzelfde patroon

Tool poisoning. Invariant Labs omschreef het in april 2025 zo: “malicious instructions embedded within MCP tool descriptions that are invisible to users but visible to AI models.” De tool-beschrijving die een gebruiker in een goedkeuringsscherm ziet, is niet gegarandeerd dezelfde tekst als wat het model leest — en het model volgt wat het leest. Hoe wijdverbreid dit werkt, mat MCPTox (augustus 2025) op 45 operationele MCP-servers en 353 echte tools.

72,8%aanvalssucces bij o1-mini, het kwetsbaarste van twintig geteste agents
<3%weigerpercentage van Claude-3.7-Sonnet — het hoogste van alle twintig

De auteurs noteren zelf waarom dit geen randgeval is: “more capable models are often more susceptible, as the attack exploits their superior instruction-following abilities.” Een aanval die op instructievolgen leunt, wordt niet zwakker naarmate een model beter instructies volgt — hij wordt sterker.

Rug pull. Bij tool poisoning is de server vanaf het begin kwaadaardig. Bij een rug pull is dat niet zo: de server is legitiem op het moment van goedkeuren, en verandert pas daarna. Check Point Research documenteerde dat patroon in augustus 2025 als CVE-2025-54136 (“MCPoison”) in de Cursor-editor: een aanvaller committet een onschuldige MCP-configuratie naar een gedeelde repository, wacht tot een slachtoffer die goedkeurt, en wijzigt de configuratie daarna in een kwaadaardig commando. Cursor voerde het gewijzigde commando uit zonder een nieuwe goedkeuring te vragen, omdat — in de woorden van de onderzoekers — “once an MCP is approved, future modifications to its command or arguments are trusted without any additional validation or prompt”, en die vertrouwensbinding gold “only to the name” van de tool-entry, niet de inhoud ervan. Cursor 1.3 (eind juli 2025) verplicht sindsdien een nieuwe goedkeuring bij elke wijziging.

Confused deputy. De derde plek zit niet in de tool-beschrijving maar in de autorisatielaag. De officiële Security Best Practices van het protocol zelf beschrijven het “confused deputy”-patroon in detail: een MCP-proxyserver die met een vaste client_id tegen een externe autorisatieserver praat, gecombineerd met dynamische clientregistratie en een consent-cookie, laat een aanvaller een autorisatiecode naar zijn eigen server omleiden zonder dat de gebruiker een tweede keer iets goedkeurt. De documentatie is expliciet over de oorzaak: de proxy behandelt toestemming als eigenschap van het gedeelde client_id, niet van de specifieke cliënt die op dat moment iets vraagt.

Wat daaruit volgt

Drie kwetsbaarheden, drie verschillende onderzoeksteams, drie verschillende lagen van het protocol — en dezelfde vorm. Een tool-beschrijving wordt goedgekeurd, niet doorlopend herlezen. Een tool-configuratie wordt vertrouwd op naam, niet op inhoud. Een OAuth-cliënt wordt herkend aan een gedeeld ID, niet aan wie er werkelijk vraagt. Steeds bindt het vertrouwen aan een label dat stabiel blijft, op een plek die is ontworpen om te veranderen.

De lethal trifecta: er hoeft geen kwaadwillende server bij te zijn

Alle drie de kwetsbaarheden hierboven vragen om iets dat kapot is — een vervalste beschrijving, een gewijzigde configuratie, een lekke proxy. Het scenario dat Simon Willison in juni 2025 de “lethal trifecta” noemde, heeft dat niet nodig. Zijn stelling: zodra een agent tegelijk beschikt over (1) toegang tot privédata, (2) blootstelling aan onvertrouwde inhoud, en (3) een manier om naar buiten te communiceren, is het resultaat exploiteerbaar — ongeacht of elke afzonderlijke tool zich precies gedraagt zoals beloofd.

MCP maakt die combinatie makkelijker dan wat eraan voorafging, simpelweg omdat het protocol bedoeld is om tools van verschillende aanbieders moeiteloos samen te voegen in één agent. Precies dat overkwam de officiële GitHub-server (14.000 sterren) in mei 2025: Invariant Labs liet zien dat een gebruiker die zijn agent vraagt “bekijk de open issues” op een publieke repository, genoeg is. Een aanvaller plaatst een issue met verborgen instructies; de agent leest het tijdens een routinetaak, wordt daardoor omgeleid, haalt gegevens uit een privérepository van dezelfde gebruiker in de context, en publiceert die — via een eigen, autonoom geopende pull request — in de publieke repository. Geen van de tools loog over wat hij deed: “issues lezen”, “privécode lezen” en “een PR openen” werkten allemaal precies zoals gedocumenteerd. Het probleem was dat één agent alle drie tegelijk mocht.

De onderzoekers waren daar zelf duidelijk over: “this is not a flaw in the GitHub MCP server code itself, but rather a fundamental architectural issue that must be addressed at the agent system level.” Willison's eigen overzicht bevestigt dat dit geen incident is maar een patroon — hij noemt vergelijkbare gevallen bij onder meer Microsoft 365 Copilot, GitLab's Duo Chatbot, Slack en Google Bard.

Wat de spec zelf al zegt

Geen van dit alles is onbekend bij wie het protocol onderhoudt — voor een deel ervan. De Security Best Practices behandelen confused deputy uitgebreid, met een compleet stappenplan en verplichte tegenmaatregelen. Ze verbieden token passthrough met zoveel woorden (“MCP servers MUST NOT accept any tokens that were not explicitly issued for the MCP server”), beschrijven SSRF via OAuth-metadata-discovery, en behandelen het compromitteren van lokale servers die met dezelfde rechten draaien als de client zelf.

Wat op diezelfde pagina — met versiedatum 2026-07-28, dus recenter dan alle drie de kwetsbaarheden hierboven — niet met zoveel woorden voorkomt, zijn de termen “tool poisoning” en “rug pull”. De onderliggende oorzaak (een tool-lijst die kan wijzigen zonder herkeuring) raakt de spec aan via notifications/tools/list_changed, maar of een client daarbij opnieuw om goedkeuring moet vragen is een implementatiekeuze, geen protocolvereiste. Cursor loste dat pas op in versie 1.3, eind juli 2025 — als productbeslissing, niet omdat de spec het voorschreef.

Vanaf hier: mijn ontwerpinterpretatie

Alles hierboven is terug te lezen in de bronnen onderaan. Wat nu volgt is waar ik voor mijn eigen werk mee zou beginnen — en het is nadrukkelijk een toepassing van een grens die dit stuk niet zelf heeft uitgevonden. Het eerdere stuk over promptinjectie concludeert dat een controle rond de handeling hoort te staan, niet stroomopwaarts bij de invoer. MCP is een van de plekken waar “de invoer” binnenkomt — een tool-beschrijving, een tool-respons — en is dus geen vervanging voor die grens, maar een extra reden om hem serieus te nemen. En toegang tot capaciteit gaat over de vraag welk model je gebruikt; MCP is de laag die bepaalt welke tools dat model daadwerkelijk kan aanraken — dezelfde vraag, een laag lager.

Wat ik hieruit zou bouwen

  • Behandel een MCP-server als onvertrouwde invoerbron, niet als een aangesloten functiebibliotheek. Zowel de beschrijving als de respons van een tool zijn tekst die van buiten komt — dezelfde categorie als de issue, de pagina, het bestand uit het eerdere stuk.
  • Toets elke nieuwe servercombinatie aan de lethal trifecta vóórdat hij live gaat, niet erna. Het GitHub-geval had geen bug nodig, alleen een combinatie.
  • Pin een tool-definitie op inhoud, niet op naam. Een hash van de beschrijving die verandert zonder nieuwe goedkeuring, is precies het signaal dat een rug pull achterlaat.
  • Schaal credentials per server af. Token passthrough is door de spec zelf verboden, en dat verbod bestaat omdat een token dat overal geldig is, ook overal fout kan gaan.
  • Vertrouw “officieel” en “populair” niet als vervanging voor gelezen code. De GitHub-server had 14.000 sterren en werkte precies zoals beloofd — dat was het probleem, niet de oplossing.

Wat dit onderzoek niet aantoont

  • Niet dat MCP onveiliger is dan een eigen, ongestandaardiseerde tool-integratie zou zijn geweest — geen van de bronnen hier maakt die vergelijking.
  • Niet dat elke MCP-server een risico is. MCPTox test hoe agents reageren op vergiftigde metadata, niet hoeveel van de 45 onderzochte servers zelf al vergiftigd waren.
  • Niet dat CVE-2025-54136 nog openstaat — die is gepatcht in Cursor 1.3.
  • Niet dat de drie beschreven patronen de volledige dreiging dekken. De spec zelf noemt aanzienlijk meer, waaronder SSRF, mix-up-aanvallen en misbruik van localhost-redirects.
  • En niet dat een geschreven MUST in een specificatie hetzelfde is als gedrag in een implementatie — CVE-2025-54136 laat precies het tegendeel zien.

Waar het ophoudt

Drie voorbehouden bij deze acht bronnen

MCPTox is één benchmark, met een eigen aanvalsontwerp. De 1.312 testgevallen zijn gegenereerd via few-shot learning op basis van drie aanvalssjablonen — representatief voor wat de auteurs bedachten, niet noodzakelijk voor wat een aanvaller in het wild zou proberen.

Eén CVE is één implementatie. CVE-2025-54136 zegt iets over hoe Cursor 1.2.4 en eerder met MCP-configuraties omging, niet over hoe elke MCP-client dat doet — sommige clients vroegen mogelijk al langer om herbevestiging.

Het protocol zelf is nog in beweging. De Security Best Practices die dit stuk citeert dragen versiedatum 2026-07-28 en noemen zelf aanvalsvormen (zoals “state handle hijacking”) die pas na de kwetsbaarheden hierboven zijn beschreven. Een deel van wat hier staat kan alweer gemitigeerd zijn tegen de tijd dat dit gelezen wordt — en een deel van wat er nu niet in staat, waarschijnlijk nog niet.

Toets het op je eigen systeem

Laat je eigen systeem inventariseren welke MCP-servers het vertrouwt, of die combinatie de lethal trifecta vormt, en of een goedkeuring daar ooit opnieuw wordt gecontroleerd.

Je gaat het vertrouwensmodel van de MCP-servers in dit systeem in kaart brengen — niet of MCP veilig is in het algemeen, maar wat er in déze opstelling aan elkaar hangt.

Stap 1 — inventariseer de servers. Welke MCP-servers zijn verbonden, van wie is elke server (jijzelf, een leverancier, een derde partij), en welke tools bieden ze aan? Noteer per server of de broncode ervan in te zien is.

Stap 2 — toets de lethal trifecta. Per combinatie van servers: heeft de agent tegelijk (a) toegang tot privé- of gevoelige data, (b) blootstelling aan tekst die een buitenstaander kan beïnvloeden — een issue, een pagina, een e-mail, de uitvoer van een andere tool — en (c) een manier om naar buiten te communiceren — een bericht versturen, een PR openen, een API aanroepen? Zijn alle drie aanwezig, benoem dan het concrete scenario waarin dat misgaat.

Stap 3 — controleer de goedkeuring. Voor elke server: gebeurt de goedkeuring van tools eenmalig bij het verbinden, of wordt de tool-definitie bij elke aanroep opnieuw vergeleken met wat is goedgekeurd? Verandert een tool-beschrijving na goedkeuring zonder nieuwe prompt, dan is dat een bevinding, geen ontwerp.

Stap 4 — volg de credentials. Welke tokens of sleutels krijgt elke MCP-server, en zijn die geschaald tot wat die ene server nodig heeft, of gedeeld met andere servers of diensten? Een token dat overal werkt, werkt ook overal fout.

Stap 5 — zoek de proxy. Is er een MCP-server die zelf weer als cliënt optreedt tegen een externe API (een OAuth-proxy)? Zo ja: bindt de goedkeuring daar aan de specifieke aanvrager, of aan een gedeeld client-ID?

Stap 6 — zeg wat je niet kunt vaststellen zonder de broncode van een server te lezen, en welke server dat betreft.

De promptknop plakt de bronnen en de vindplaats van dit artikel eronder.

Bronnen

  1. Introducing the Model Context ProtocolAnthropic · Officiële aankondiging, 25 november 2024 · 2024
  2. Architecture overview — Model Context ProtocolModel Context Protocol (projectdocumentatie) · Officiële documentatie, protocolversie 2026-07-28 · 2026
  3. Security Best Practices — Model Context ProtocolModel Context Protocol (projectdocumentatie) · Officiële documentatie, protocolversie 2026-07-28 · 2026
  4. The lethal trifecta for AI agents: private data, untrusted content, and external communicationSimon Willison · simonwillison.net, 16 juni 2025 · 2025
  5. MCP Security Notification: Tool Poisoning AttacksInvariant Labs · Invariant Labs blog, 1 april 2025 · 2025
  6. GitHub MCP Exploited: Accessing private repositories via MCPInvariant Labs · Invariant Labs blog, 26 mei 2025 · 2025
  7. MCPTox: A Benchmark for Tool Poisoning Attack on Real-World MCP ServersWang et al., negen auteurs · arXiv:2508.14925 [cs.CR], ingediend 19 augustus 2025 · 2025
  8. Cursor IDE's MCP Vulnerability (MCPoison, CVE-2025-54136)Check Point Research · Check Point Research blog, 5 augustus 2025 · 2025

Verder lezen