MCP and Agent Security
The OWASP MCP Top 10, Explained in Practice
The OWASP MCP Top 10 is an open, community-driven reference for the ten most critical security risks of MCP servers. It gives teams a shared language for attack surfaces that did not exist before the Model Context Protocol. This guide takes each of the ten risk classes and adds practical depth: what goes wrong at the core, how you test for it, and which structural countermeasure holds.
- Missing transport authentication, which turns the server into a confused deputy.
- A broken OAuth trust anchor, because a single library defect is inherited by every downstream server.
- Indirect prompt injection, where injected content triggers the server's own tools.
- Controls that only look like controls, whose implementation can be bypassed (false assurance).
- SSRF, command injection, secret leaks, over-broad permissions, insecure defaults, path traversal, and DoS.
Why MCP needs its own Top 10
The Model Context Protocol wires AI agents to tools, data, and APIs. The MCP server sits at that seam and runs with the privileges and credentials of its operator. So MCP servers inherit classic server vulnerabilities on the one hand, while two new attack surfaces open up on the other: the client, which a malicious server can drive through reverse-channel verbs, and the shared libraries underneath, where a single defect reaches every dependent server. A shared taxonomy helps name these boundaries cleanly. The overview below deliberately describes the classes at the class level and names no specific vulnerable products.
The ten risk classes in detail
1. Missing or weak transport authentication
The most common mistake. A server binds an HTTP or SSE endpoint with no auth, trusting that it only listens on localhost. Any local process, and via DNS rebinding any drive-by web page, can then drive the tools with the operator's credentials. The server becomes a confused deputy: it mistakes "the request arrived" for "the request is authorized." A typical coding error is an inexact host check via startsWith instead of exact equality, which a host like 127.0.0.1.example.test slips past.
Test: Does every transport entry point authenticate? Are host and origin checked exactly? Does the server bind to loopback only by default? Fix: a per-installation secret on every route, an exact host allowlist, a bind to 127.0.0.1, fail closed.
2. Broken OAuth trust anchor
MCP adopted OAuth 2.1 and with it inherited a class of trust-root errors. If the authorization server, the protected resource metadata pointer, or the scope is taken from a client-influenced source without pinning origin and issuer, a malicious or interposed resource server can redirect the grant to its own infrastructure, harvest the token, or inject an overly broad scope into the consent. This class has the highest leverage, because the pointer often comes from a server-controlled WWW-Authenticate header and a single library defect is inherited by every dependent server.
Test: Is the origin of the authorization server and metadata firmly pinned? Is a cross-origin metadata pointer rejected? Is the scope intersected and the token audience validated? Fix: pin the origin server-side, enforce the RFC 9728 same-origin rule, intersect the scope with the entitlement, and validate aud. Details in the guide to MCP and OAuth.
3. Indirect prompt injection and tool poisoning
Tools return untrusted content (web pages, repo files, database rows, documents) verbatim into the agent context. Instructions hidden inside reach the server's own writing, executing, or fetching tools. Private data access plus untrusted content plus an outbound channel produces exfiltration, the so-called lethal trifecta. The tricky part: the attacker never talks to the server directly. More on this in the guide to prompt injection.
Test: Is untrusted tool output clearly delimited and labeled as data? Can injected content reach a state-changing or outbound tool? Fix: enforce the core rule of the threat model, that tool output is data and never a control channel. Confirmation for destructive and outbound tools, and separation of read rights from action rights.
4. Controls that only look like controls (false assurance)
A server advertises a control whose implementation can be bypassed: a command whitelist, a code sandbox, a read-only mode, a denylist. The most dangerous case is the one where the security promise is the gap itself, because nobody questions the control once it exists. Two common bypasses: type confusion, where an array or object slips past a string-only check, and missing normalization, where a Unicode-equivalent form (fullwidth, homoglyph) sneaks past a byte comparison.
Test: attack the control directly, whitelists via other flags, sandboxes via introspection, text checks via encoding and normalization. Fix: enforce structurally rather than textually, canonicalize and check the type before the check, default deny, and reject non-conforming types.
5. SSRF and unfiltered fetches
A tool loads a caller-supplied URL with no scheme or host allowlist and no block on internal addresses, and often returns the response body verbatim. The targets are cloud metadata at 169.254.169.254, internal hosts, or file://. MCP has two data surfaces, tools and resources, and SSRF shows up on both. The most common silent bug is a gate that passes on a parse error instead of rejecting.
Test: Can a tool reach internal addresses or file://? Is there an allowlist? Does the gate fail open? Fix: a scheme and host allowlist (https only), block loopback, link-local, and private ranges including IPv6 forms, resolve DNS and pin the IP, turn redirects off, fail closed.
6. Command and code injection up to RCE
Agent-controlled input reaches exec, eval, a subprocess, or a sandbox that is not one. These are the sharpest severities in the catalog, because a single tool call leads to full server compromise. Related sinks are insecure deserialization, unsafe YAML, template injection, and XXE. The pattern is always the same: an untrusted tool argument reaches a powerful interpreter or parser.
Test: Does a tool parameter reach a shell, an interpreter, or a deserializer? Fix: never hand agent input to a shell, argv only plus an allowlist without shell=True, real isolation with no network, a real parser instead of eval() for expressions, default deny for execution.
7. Secret and credential exposure including the reverse channel
Operator credentials sit next to untrusted code, tokens get logged, echoed, or forwarded unchecked to a foreign destination, TLS verification is off. New in this class is the reverse channel: the MCP verbs sampling/createMessage and elicitation/create let a malicious server drive the client and pull out private context or auto-answered secrets. Here the vulnerable party is the client, which is why server-side-only reviews structurally miss this class.
Test: Does a tool output or forwarded request carry a secret? Does the client bind private context into a server-initiated sampling request or auto-fill an elicitation? Fix: broker calls on the host side, never log or pass secrets through, and require explicit consent per server-initiated operation.
8. Over-broad capability and missing per-call authorization
Tools can do far more than the use case needs, for example the whole file system or arbitrary SQL, and authorization is applied unevenly across the protocol surface. The underlying pattern is shared with BOLA and IDOR: access to an object without an ownership check. Newer protocol verbs belong here too, for example a completion endpoint that enumerates privileged values without the real tool's authorization, or a batch that smuggles in a forbidden method unchecked.
Test: Does the same authorization apply to every method and every batch element? Is a capability broader than its use case? Fix: scope every tool to the minimum, authorize every method and every batch element identically, deny by default.
9. Insecure defaults
A 0.0.0.0 bind, a wildcard allowlist, DNS rebinding protection off, sandbox bypass flags on, shipped or known signing keys, default admin credentials. The attack needs no exploit, just the unchanged default. This class is also the root of class 1: the open bind and the wildcard host are what make the confused deputy possible in the first place. Accepting alg=none on JWTs or generating tokens predictably belongs here too.
Test: Does the server also start in an insecure combination? Are privileged or debug tools shipped enabled? Fix: safe by default, explicit opt-in for dangerous modes, refuse to start on obviously insecure combinations, generate tokens with a CSPRNG.
10. Path traversal, insecure file access, and DoS
Agent-supplied paths escape the root via ../, absolute paths, symlinks, or normalization, an archive entry writes out of the target folder via zip slip, or a tiny payload expands with no size limit and exhausts the host's memory and CPU. Path traversal too shows up on both data surfaces, tools and resources.
Test: Does the resolved path stay under the root? Is a symlink rejected? Is the decompressed size capped? Fix: canonicalize plus a containment check against the real root, reject symlinks and .. segments, hard-cap the decompressed size and count bytes while streaming, add timeouts plus process-level recovery.
From the checklist to a reliable proof
Knowing a class is not the same as knowing the control holds in your server. Some of these classes are detectable statically as a code pattern or config literal, for example insecure defaults, SSRF sinks, or injection paths. Another part lives in the design of capabilities and trust boundaries, for example prompt injection or ownership checks, and only becomes visible in the behavior of the agent loop. Reliable security therefore needs both layers: a static check against defined invariants and a dynamic check that backs each individual finding with an executed proof rather than merely assuming it.
Where does your MCP server stand across these ten classes?
In a free initial call, we map your server to the risk classes and name the one property your product depends on and whether it is demonstrable. Every finding comes with a proof of concept, not a guess.
Book a free callCyberSec42 is an independent technical security assessment, not an accredited certification body.
Frequently asked questions
What is the OWASP MCP Top 10?
The OWASP MCP Top 10 is an open, community-driven reference for the ten most critical security risks of MCP servers. It gives developers and security teams a shared language for the MCP-specific attack surfaces.
Why does MCP need its own Top 10 and not the web Top 10?
MCP servers inherit classic server vulnerabilities but bring two new attack surfaces: the client, which a malicious server can drive through reverse-channel verbs, and the shared libraries underneath, where a single defect reaches every dependent server. An MCP-specific taxonomy captures these boundaries more precisely.
Which of the ten classes is the most dangerous?
It depends on the server. Missing transport authentication is the most common, a broken OAuth trust anchor has the highest leverage because a library defect is inherited by every dependent server, and command injection has the sharpest severity because one tool call leads to full compromise.
Can I cover all ten classes with a scanner?
No. Some classes are detectable statically as a code pattern or config literal, others live in the design of capabilities and trust boundaries and only become visible in the behavior of the agent loop. The check only becomes reliable with an invariant-based analysis plus an executed proof per finding.
How do I start hardening?
Control three things: who is allowed to talk to the server, what it is allowed to do, and which inputs it trusts. The practical starting point is in the guide to securing an MCP server.