Agent-in-the-Middle: what's wrong with unsigned A2A agent cards
The A2A protocol lets agents discover each other at runtime. An agent advertises itself with an Agent Card — a JSON document served at a well-known path, /.well-known/agent-card.json, listing its endpoint, skills, capabilities, and auth requirements. A host agent fetches that card, reads what the remote agent claims to do, and routes matching tasks to it.
The entire trust decision rests on a file the host did not author. That is the problem.
The attack
Coined by SpiderLabs in 2025, Agent-in-the-Middle (AITM) is ARP spoofing moved up the stack. Instead of poisoning a routing table, you poison agent discovery. The recipe:
- Stand up a rogue agent and publish an Agent Card whose declared skills make it look like the best fit for the traffic you want — broad, confident, capability-rich.
- Get the host agent to discover it. A forged card at a typosquatted or compromised domain is enough; the host performs discovery and trusts what it finds.
- The host now routes sensitive tasks to you. You sit in the path like an old-school packet sniffer — except you can also return false results, which the downstream LLM or user then acts on.
It does not stop at interception. An active AITM agent shapes the conversation: it can exfiltrate the task payload, alter the answer, and steer the workflow, all while looking like a legitimate node. This is ASI07 (Insecure Inter-Agent Communication) with a side of ASI04 (Agentic Supply Chain Compromise) — a malicious participant introduced into the fabric through discovery.
The fix everyone names
The A2A spec defines AgentCardSignature: a JSON Web Signature (RFC 7515) over the card content, canonicalised with JCS (RFC 8785) so the signed bytes are reproducible across JSON serialisers. A2A v1.0 — now governed under the Linux Foundation — made signed cards the headline change, because without them decentralised discovery has no trust model at all. The a2a-sdk ships sign/verify helpers. Clients should reject unsigned cards from public agents and check the signer’s key against a trusted registry.
So sign your cards. Done?
The fix everyone ships, broken
Two failure modes turn a signed deployment back into a vulnerable one:
Verify-once-at-boot. A card pulled from a CDN and verified at startup can be swapped by the time a task actually routes through it. Signature verification at first fetch proves nothing about the card you’ll use an hour later. Re-verification has to be periodic — per task, or on a short schedule — never a one-time check at boot.
Treating signed as safe. A valid signature proves the card came from the claimed issuer. It says nothing about whether that issuer is benign, and nothing about the content. Every field on an external card is still untrusted input and must be sanitised before it reaches an LLM — a skill description is a perfectly good place to hide a prompt injection.
And the unglamorous one: hostname validation. In informal audits of production agent systems this year, the majority skipped checking that the certificate’s SAN matched the expected service name. Signed cards over a connection you never authenticated is the worst of both worlds.
How MST audits it
mas-sentry-toolkit treats the A2A surface as a first-class target. The A2A client module checks, against each discovered agent:
$ mst a2a scan https://target.local
[discovery] /.well-known/agent-card.json ....... 200
[signature] AgentCardSignature ................. MISSING
[transport] TLS 1.3 · SAN match ................ ok
[schema] card fields validated .............. 2 skills unsanitised
[ASI07] insecure inter-agent comms · unsigned agent card
report → out/target.sarif
The probes map directly onto the failure modes above: presence and validity of the card signature, whether the signer resolves to a trusted key, transport authentication and SAN matching, and schema validation of every field before it would reach a model. Findings come out as SARIF, so an unsigned-card result lands in the same pipeline as the rest of a security review — mapped to ASI07, next to everything else.
Signing the card is necessary. Verifying it correctly, every time, over a connection you actually authenticated — that is the part worth testing for.