SW-A2A-013: Signing key not bound to the card's host
Last updated 7 November 2026
| Fact | Value |
|---|---|
| Severity | Medium |
| Checks | A2A agent card |
| Malicious indicator | No |
| OWASP Agentic Top 10 | ASI07 |
| OWASP MCP Top 10 | None mapped |
| NIST SP 800-53 | SI-7 (provisional mapping to SP 800-53 base controls) |
| First shipped in | Scanner 1.3.0 |
What it detects
The rule looks at the protected header of each signature. It reports a jku (the address of a key set) whose host is neither the card's host nor a subdomain of it, with the reason off_host and the host in the evidence. For a card fetched from a remote agent, the card's host is the host that served it; for a card in the repository, it is the host of each interface url.
It also reports a signature whose only key source is a key carried in the header itself (jwk, x5c or x5u, with no jku), with the reason embedded_key. A key carried in the header is reported only when no key file is named by a2a.keys, because once one is named it is the only source of keys. The rule does not check the signature; SW-A2A-010 does that.
Why it matters
A key is evidence about the owner only when it comes from somewhere the owner controls. A key set on an unrelated host tells a client only that whoever runs that host signed the card. A key carried inside the signature header is chosen by whoever wrote the card, so anyone can change the card, sign it with a new key and include that key. Neither says who stands behind the agent. The scanner maps this rule to OWASP ASI07, Insecure Inter-Agent Communication, and to NIST SP 800-53 control SI-7, Software, Firmware, and Information Integrity.
Example
A card with two signatures. The header of the first names a key set on keys.other.example.invalid, which is not the host of the card's endpoint, agent.example.invalid. The second carries its public key as a jwk in the header:
.well-known/agent-card.json
{
"name": "demo-agent",
"description": "Drafts release notes from merged pull requests.",
"version": "1.0.0",
"supportedInterfaces": [
{
"url": "https://agent.example.invalid/a2a",
"protocolBinding": "JSONRPC",
"protocolVersion": "1.0"
}
],
"capabilities": {},
"securitySchemes": {
"bearer": {
"httpAuthSecurityScheme": {
"scheme": "Bearer"
}
}
},
"securityRequirements": [
{
"schemes": {
"bearer": {
"list": []
}
}
}
],
"defaultInputModes": [
"text/plain"
],
"defaultOutputModes": [
"text/plain"
],
"skills": [
{
"id": "release-notes",
"name": "Release notes",
"description": "Drafts release notes for one tag.",
"tags": [
"release"
]
}
],
"signatures": [
{
"protected": "eyJhbGciOiJFUzI1NiIsImtpZCI6ImRlbW8ta2V5Iiwiamt1IjoiaHR0cHM6Ly9rZXlzLm90aGVyLmV4YW1wbGUuaW52YWxpZC9qd2tzLmpzb24ifQ",
"signature": "eyxzfAf265vpGg6qvgafIm5UMQc2va0JKahy32odqLctZeK-4mG6U5eQfInF0KRvyqxdl_txi2dxNZcK8f4OHg"
},
{
"protected": "eyJhbGciOiJFUzI1NiIsImtpZCI6ImlubGluZS1rZXkiLCJqd2siOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiJsY04tbWtwaTFWb3BxWmFwT0NxWjU0clFWbWVndkJjQWNCUFdUdkpUZHo0IiwieSI6InZVaTAyYW1vMDRsODQyTVFObDRkUElGdGtNNV9SckYwa0pGUWphR1RGUW8iLCJraWQiOiJpbmxpbmUta2V5In19",
"signature": "e8PdeBbjAdfatFMaASwA-_5ffS9MKAgi2C8t6RwJMVM04qLVoj7iL8b7SjAH1Zeh8EmVE2RW61bzVvl3vNj2Tw"
}
]
}The scanner reports the two entries separately: the first as a key set on another host, the second as a key carried in the signature header. Both signatures were made with throwaway keys for this example.
How to fix
Publish the key set on the card's own host or a subdomain, for example at /.well-known/jwks.json, and name it in jku. Better still, list the public key in the file that a2a.keys names, which is the only source the scanner trusts once the setting is present. Remove jwk, x5c and x5u from the protected header, and sign again after changing the header, because the header is part of what is signed.
If it is a false positive
A key service on its own domain, such as one run by your identity provider, is a different host and is reported even when you control it. If you control the key set, accept the finding with a note, or copy the public key into the repository's key file and set a2a.keys, which makes the jku irrelevant. A jku on a subdomain of the card's host is not reported.
If the finding is not a problem in your case, accept it with a note: in the dashboard a manager or owner can accept a finding and the note is kept with it; with the scanner on its own, add it to the baseline file with a reason and an expiry date.