Question re: PeerTube's pubkey IDs

As part of security checking for incoming events, we check that the keyId sent in the HTTP signature is actually owned by the actor that the activity came from. This is to guard against activity spoofing from separate users at the same server (e.g. user B@server pretends to send a Create(Note) from user A@server).

Our check is pretty simple, the keyId matched against the public key id as retrieved from the actor.

Except it fails for PeerTube because:

  • PeerTube's actors all have the #main-key suffix on their public key IDs (e.g. https://tilvids.com/accounts/thelinuxexperiment#main-key)
  • The HTTP Signature's keyId does not include the #main-key suffix (e.g. https://tilvids.com/accounts/thelinuxexperiment)

So the key ownership cross-check fails.

I could adjust the logic to strip out the URL's hash, but I was wondering if that was actually secure. I assume this is what is already done since PeerTube successfully federates with other softwares.

1 Like

ideally peertube would use keyId to link to the key and not to the actor, but i would guess that the way that this is handled in other softwares is to implicitly accept both keys and actors. so don’t just always strip the hash, but instead only strip it for the initial HTTP GET. maintain the hash when checking for key id. but you’ll still have to handle the invalid case where the keyId is actually an actor.

@julian Yes, this is secure because web origin remains the same. I simply remove the fragment, it works for everything except GoToSocial.

Nevertheless, mismatch between signature keyId and publicKey.id is a bug.

cc @peertube

:sweat_smile: Thanks all;

  • @thisismissem@hachyderm.io I wasn't actually sure which spec this was a part of (although to be honest I also wasn't planning on reading through a spec to find out)
  • @trwnh@socialhub.activitypub.rocks this is for verification on receipt of activity, not for an unsolicited GET from me. Didn't think it was a link to the actor id, but that makes sense!
  • @silverpill@mitra.social yeah origin based security deems it safe, but I don't happen to like that security model, personally. I suppose that is the tradeoff... Plus someone could just assign the same pubkey to every user, rendering the whole exercise moot.

@julian I think URL comparison should not be done as a string. Like @silverpill said, fragments are stripped before comparison, alongside the usual other considerations (normalized query parameters, UTF and case normalization for the hostname, etc)

I don’t see how same web origin makes this secure again one actor signing an activity for another actor at the same origin (scheme, hostname, port). However, I agree that the workaround/hack for this bug is to strip the fragment, if any, and check if the result is the same as the actor URI specified in the activity.

sorry, to clarify, i meant the initial HTTP GET of the keyId. meaning that, if the keyId contains a fragment component, you perform the HTTP GET against the keyId minus the fragment component.

@stevebate

>I don’t see how same web origin makes this secure again one actor signing an activity for another actor at the same origin (scheme, hostname, port).

Because only location of a key matters. This is specified in FEP-fe34, and I just published a more detailed explanation of how it all works:

https://mitra.social/objects/01941d35-5808-e5f6-965a-44283da438ea

@NodeBB

After rereading that FEP, I think I understand better why we have different perspectives on this topic. First, “web origin” is not the same as the AP “same origin policy” described in the non-normative AP authz/authn primer. These two kinds of “origins” are not computed with the “same algorithm” as claimed by the FEP (or at least it appears to me that this is the claim, it’s not completely clear). In this thread, it seems to me that you are using “origin” in the “web origin” sense. If not, please clarify what you mean.

The other issue is that the FEP doesn’t appear to be consistent with the W3C SocialCG group report on HTTP Signatures. This document describes an algorithm for verifying an actor/key relationship that does not depend on (or require) either the same web origin for actors and keys or the AP same origin policy. The AP “same origin policy” appears to be described in an authorization policy context rather than for HTTP signature authentication.

The inconsistency I see is that the FEP requires “embedded” objects to have same web (?) origin as the containing object. Keys may or may not be embedded as part of the serialized actor graph. They may only be referenced by URI (even Mastodon supports it), but if they happen to be embedded in the actor graph serialization, I know of no good reason to apply the FEP origin-related constraints to the URIs.

What’s important for verifying the actor/key relationship is the mutual references between the actor publicKey and the key’s owner (or controller) property. The key itself could be served from a different web origin than the actor document. Furthermore, there’s no guarantee that removing the key’s URI fragment, if any, will lead to successfully verifying the actor/key relationship, even if the relationship is valid. For example:

key URI: https://keyserver.example/bob/keychain#B54F15A0
actor URI: https://server.example/bob

As long as, the document retrieved from https://server.example/bob has a publicKey property referring to the key URI and the key object has an owner or controller property referring to the actor URI, it’s a valid relationship.

In other words, removing or ignoring the key URI fragment will not generally work. In this example, dereferencing the key URI might result in a key chain object with several public keys having different fragment identifiers. Even more conventional AP actor documents might have multiple public keys with different fragment identifiers (mentioned in the SocialCG report). Ignoring key URI fragments will not work in this case either.

2 Likes

@stevebate I'm using the term "origin" as defined in RFC-6454. This is also what FEP-fe34 refers to.

FEP-fe34 doesn't require embedded object to have same origin as the containing object (but it says that embedded object with a different origin shouldn't be trusted).

FEP-fe34 doesn't mention actor/key relationship because location of an actor document is not important for authentication via HTTP signatures. Only location of a key is important.

However, the actor/key relationship is important for authorization: "Can that actor own this public key?". FEP-fe34 currently doesn't provide a direct answer to this question, but it recommends the same origin policy as a general principle. According to this principle, actors shouldn't own objects on different servers, and therefore public key should have same origin as actor. As far as I know, all existing implementations behave in this way and don't put public key on a different server than actor. I also don't see any reason to do otherwise.

If some document contradicts both FEP-fe34 and implementer consensus, it is probably not correct and should be fixed. I can look into it if you point to a specific paragraph or sentence.

@NodeBB

@stevebate I'm using the term "origin" as defined in RFC-6454. This is also what FEP-fe34 refers to.

FEP-fe34 doesn't require embedded object to have same origin as the containing object (but it says that embedded object with a different origin shouldn't be trusted).

FEP-fe34 doesn't mention actor/key relationship because location of an actor document is not important for authentication via HTTP signatures. Only location of a key is important.

However, the actor/key relationship is important for authorization: "Can that actor own this public key?". FEP-fe34 currently doesn't provide a direct answer to this question, but it recommends the same origin policy as a general principle. According to this principle, actors shouldn't own objects on different servers, and therefore public key should have same origin as actor. As far as I know, all existing implementations behave in this way and don't put public key on a different server than actor. I also don't see any reason to do otherwise.

If some document contradicts both FEP-fe34 and implementer consensus, it is probably not correct and should be fixed. I can look into it if you point to a specific paragraph or sentence.

@NodeBB

Thanks for the clarification.

I was referring to the Athentication section that states four conditions (none of which are satisfied for an embedded actor key from a different web origin) and says:

If none of these conditions are met, the object MUST be discarded.

If the embedded “object” is the key in the actor fetch scenario, discarding it wouldn’t be desirable. If you intended to suggest that the key/object should be dereferenced for verification, the wording should be changed.

After rereading the FEP Authentication several times, I think( I see what you are trying to specify. You want the HTTP Signature key web origin to be the same as a POSTed activity URI web origin (but it could be different than the actor URI web origin?). However, I don’t know why you believe that specific key/activity web origin restriction is necessary.

To the extent the FEP is documenting existing practices (although using requirements language), that’s fine. Developers should be aware that the way Mastodon-like servers have implemented AP+Signatures is not the only valid way to do it. Like I said before, even Mastodon doesn’t appear to have the restrictions that you are proposing (for incoming posted activities).

I’m not sure it contradicts the SocialCG’s HTTP Signature, but it adds unnecessary and undesirable restrictions to it. You can also take a look at Mastodon’s handling of HTTP Signature key verification to explore how the FEP differs from current practice. It might also be useful to have a section discussing the similarities and differences with current popular server implementations.

Also, about web origin… the AP specification uses the term “origin” in a very ambiguous way. It doesn’t reference web origin (RFC-6454) at all. Some references to “origin” appear to mean the “actor who originated an activity”. For example,

The receiving server MUST take care to be sure that the Update is authorized to modify its object. At minimum, this may be done by ensuring that the Update and its object are of same origin.

This could be interpreted as the Update should originate from the same actor as the object being modified, which is reasonable. That interpretation doesn’t require the actor and the object URIs to have the same RFC-6454 web origin.

Again, it’s very ambiguous so it’s possible to have different interpretations. However, I don’t agree with some parts of the FEP-fe34 interpretation.

2 Likes

@julian Did you report this bug to PeerTube?

@julian Did you report this bug to PeerTube?

@silverpill@mitra.social not yet, although I should open an issue, thanks for the reminder!

1 Like

@julian I am affected by it as well, which is strange because previously federation with PeerTube worked fine. Perhaps they broke it in a recent release

@julian I am affected by it as well, which is strange because previously federation with PeerTube worked fine. Perhaps they broke it in a recent release

Yes, it should be dereferenced. This is currently covered in “Emdedded objects” section, but I see how that might be confusing. I submitted a pull request that moves this requirement closer to the list of authentication methods: #468 - WIP: FEP-fe34: Clarify authentication procedure - fediverse/fep - Codeberg.org.

In the context of authentication, activity ID and actor ID may have different origins. However, the second part of the FEP (titled “Authorization”) prohibits this:

Identifier of an object and identifier of its owner MUST have the same origin.

In other words, actor MUST NOT own objects from different origin.

This restriction comes from the origin-based security model described in the FEP. When an HTTP signature is verified, the trust chain is established: activity → public key (via keyId parameter) → server.

To remove this restriction, some kind of cross-origin verification mechanism is needed. So far, I have not seen any evidence that such mechanism is actually necessary. Unnecessary complexity should be avoided.

FEP-fe34 is based on existing practices, but it doesn’t describe exact behaviors, which vary significantly between implementations. If Mastodon is not compliant with FEP-fe34, that might not be a problem, sometimes the risk is quite low (e.g. authentication of emojis). I suspect that some popular server implementations are vulnerable to cache poisoning attacks, but I don’t plan to audit them.

FEP-fe34 needs to provide clear instructions to developers, so I used RFC-6454 as a reference. ActivityPub spec is useless when it comes to authentication and authorization. I don’t think its authors understood these aspects, otherwise there would be no need for FEP-fe34. Even developers who are very experienced with AP have limited understanding (ActivityPub was published in 2018; GHSA-3fjr-858r-92rw, which affected many projects, was published in 2024).

I would argue that there is an entire class of errors and vulnerabilities that arise from the lack of proper authentication/authorization that are not solved by solved by same-origin, and in fact might be made worse by it!

By my understanding, the cited GitHub security advisory deals with fetching a document from some location, while describing a subject whose id that does not match that location.

There is nothing inherently wrong with this! The claim you should be verifying is a different claim than the one that is actually being verified:

  • Incorrect: The current document is an authentic representation of the current resource.
  • Correct: The current document is authorized to make authentic statements about a given resource.

Understandably, it is easier to establish the former than the latter; we simply assume that anything can make authentic claims about itself. The descriptor document served at the URI in the id is conflated with the thing itself.

Trying to establish the latter usually goes no further than same-origin at most. If you assume that every URI on a given authority has the same controller, then this is fine. But this is a false assumption, because URI authority can be delegated at any point along the path, for example by configuring a web server to pass requests on a certain prefix to a different application entirely. /media might be served by a different application entirely than /users. This shouldn’t be a problem, but it is a problem because the real authority is never specified; the same origin assumption is used instead of establishing any real authority.

The error in logic that caused the vulnerability was conflating the two claims. Instead of passing down the document location, Mastodon passed down the id, because Mastodon assumed they would always be the same, but they are not always the same.

In fact, the related security advisory GHSA-jhrq-qvrm-qr36 demonstrates another fallacy of the “same-origin” line of thinking. The way that this security advisory is framed is actually papering over the flaws of same-origin instead of addressing the actual underlying issue. The assumptions being made by fediverse software aren’t being corrected, they’re just being augmented with other assumptions which may themselves be incorrect. The end result is a far more complicated security model built on quirks. Whenever the next vulnerability happens due to same-origin being insufficient, it will probably lead to yet another ersatz requirement being placed on what constitutes a “valid” or authentic activity, object, post, profile, key, etc.

I don’t see a good reason to continue placing such unnecessary requirements. Identity servers and keyservers shouldn’t be discarded entirely and for no good reason; this just creates unnecessary fragility where a bidirectional claim is sufficient (actor declares key as representative; key declares actor as controller). Actors and their objects shouldn’t be locked to the same domain; this unnecessarily creates barriers to real distributed systems and to cross-domain migration.

In short, “same origin” is a model that might be sufficient for centralized models of security, but it is insufficient for true decentralized security. I don’t think it’s a good idea to enshrine the assumption that a single server rules everything on that domain, or that there is only one such server possible.

Instead, we should strive to recognize not just the properties of objects, but also who is claiming these properties. Before merging graphs or datasets, we should establish that they (or their associated document) are trustworthy about the claims contained within. This is in effect what “object proofs” should be doing – signing the graph or subgraph containing only statements about a single subject.

1 Like

@julian Fixed: https://github.com/Chocobozzz/PeerTube/commit/038c41030858a98071200cdbb2b134aeaac7be9a

1 Like