When resolveFetchedKey in packages/fedify/src/sig/key.ts fetches the document at a keyId URL and parses it as a standalone CryptographicKey or Multikey, it does not check whether the object's id matches the requested URL:
let key: T | null = null;
if (object instanceof cls) key = object;
else if (isActor(object)) {
// …iterates keys and matches k.id?.href === keyId
}
The actor branch compares each candidate with k.id?.href === keyId before accepting it. The standalone branch skips that check. A document served at one URL can therefore declare a different id and still be cached under the requested URL.
This came from a security report, but we closed the report because the mismatch does not provide a way to impersonate another actor. The verification path does not use the key document's id to establish the actor's identity. It checks the key's owner or controller against the activity's actor.
The current behavior is nevertheless confusing: key.id may differ from the keyId used for the lookup and cache entry. For a standalone key with a present but mismatched id, we could reject the document or emit a warning.
The actor branch intentionally allows one related case. When the requested keyId has no fragment and the actor exposes exactly one key, it accepts that key without an exact id match:
const keyIdUrl = new URL(keyId);
if (key == null && keyIdUrl.hash === "" && length === 1) {
key = lastKey;
}
A change to the standalone path should account for this existing tolerance rather than making the result depend unexpectedly on whether the response is an actor or a key document. I am not yet sure whether a mismatch should be an error. Logging it is less likely to break interoperability, but rejecting it keeps the fetched key's identity aligned with its cache key.
Acceptance criteria:
- A standalone key document with a present
id that differs from the requested keyId is rejected or handled according to a documented and tested rule.
- The fragmentless single-key tolerance in the actor branch continues to work.
- A regression test covers a standalone key document with a mismatched
id.
When
resolveFetchedKeyin packages/fedify/src/sig/key.ts fetches the document at akeyIdURL and parses it as a standaloneCryptographicKeyorMultikey, it does not check whether the object'sidmatches the requested URL:The actor branch compares each candidate with
k.id?.href === keyIdbefore accepting it. The standalone branch skips that check. A document served at one URL can therefore declare a differentidand still be cached under the requested URL.This came from a security report, but we closed the report because the mismatch does not provide a way to impersonate another actor. The verification path does not use the key document's
idto establish the actor's identity. It checks the key's owner or controller against the activity's actor.The current behavior is nevertheless confusing:
key.idmay differ from thekeyIdused for the lookup and cache entry. For a standalone key with a present but mismatchedid, we could reject the document or emit a warning.The actor branch intentionally allows one related case. When the requested
keyIdhas no fragment and the actor exposes exactly one key, it accepts that key without an exactidmatch:A change to the standalone path should account for this existing tolerance rather than making the result depend unexpectedly on whether the response is an actor or a key document. I am not yet sure whether a mismatch should be an error. Logging it is less likely to break interoperability, but rejecting it keeps the fetched key's identity aligned with its cache key.
Acceptance criteria:
idthat differs from the requestedkeyIdis rejected or handled according to a documented and tested rule.id.