Comparing server-side and client-side generation of the HTTP signature header in ActivityPub

Please contribute at the github repository cristianolongoodhl/clientsidehttpsignotes

Activity delivery in ActivityPub

As reported in the ActivityPub specification, an ActivityPub server must provide each Actor with two 'Collection's: outbox and inbox. In addition, the addresses of these collections must be listed in the publicly available Actor description.

Let us call target actors of an Activity all the Actors to which the Activity has to be delivered. Target actors of an activity are specified in the activity itself as described in the specification at 6.1 Client Addressing.

Now let use recall the Activity delivery process in ActivityPub.

Activity delivery diagram

Notice that security mechanisms for both client-to-server and server-to-server communications are not specified in the protocol, as they are left to implementations. The ActivityPub specification refers (in a non-mandatory way) to the Social Web Community Group Authentication and Authorization best practices report, which effectively guided the most part of the ActivityPub implementations currently available.

For client to server communications, the report suggests OAuth 2, which is a well-established authentication and authorization protocol which, in addition, provides confidentiality features.

For server to server communications, the approach proposed by the report is to sign messages with HTTP Signatures as follows: - of course, the headers taken into consideration by the signature must contains at minimum the message content digest, see also Note 2; - the message must be signed with a key associated to the sender actor; - the keyId field of the Signature header should link to the Actor description which, in turn, must provide the public part of the actor key via a publicKey property.

Then, the receiving server can check message integrity with fetching the actor public key through keyId and checking the message body (digest) against this public key.

Note 1

This part of ActivityPub is quite controversial as in ActivityPub every Activity is an Object, so that one cannot deduce that an Object is not an Activity if not explicitly stated.

Note 2

Some implementations such as, for example, Mastodon require to take into consideration also the date header in the signature, in order to handle and eventually discard old messages which may be sent by malicious servers.

HTTP Signature in Mastodon

As reported in How to implement a basic ActivityPub server (see also Note 2) Mastodon servers defines a 30 seconds validity interval for messages. I.e., mastodon servers will discard messages received 30 seconds later the date reported in the date header.

A mastodon servers acts as a key holder for users' key pairs: key pairs of users are generated, stored and guarded by the server, which will use them to generate the signature header when delivering activities.

Activity delivery with server side signature diagram

This approach has some advantages: - [SA1] the client can delegate the automated generation and delivery of some kind of activities, such as for example the Accept activity for follow requests; - [SA2] the server can defer the signature generation and the subsequent delivery of the activity under particular circumstances, for example in case overloading which limits the currently available resources; - [SA3] the server can retry the delivery of an activity to a target in box in case of network failures.

However, it has some relevant drawbacks:

Client-Side signature

Now, let us now consider a setup where the signature header is generated by the client. In such a setup, the outbox must receive from the client the date and the signature headers, aside with the activity to be sent. Then, the outbox forwards these headers, with in addition the activity digest and the activity itself, to the activity target inboxes.

Activity delivery with client side signature diagram

Little ActivityPub Server is a proof of concept of such approach, as it provides a javascript-based form to generate the signature and send the required headers and the activity to an outbox.

Obviously, this approach does not require that the server knows client private keys. Instead, it shouldn't so that [SD1] and [SD2] cannot hold anymore. More in details: - [CA1] the server has no way to create a spurious message and claim that the message has been produced by one of this clients, as in [SD1], because it cannot sign messages using client keys; - [CA2] if the server is compromised, as in [SD2], no client key is compromised, just because the server does not hold private key of any of its clients; - [CA3] when a client migrate to another server, she could provide proofs that the new and the old accounts are owned by the same client with proving that the new account can sign messages with the same key of the old one.

Aside these advantages, the approaches based on client-side generation has some relevant drawbacks.

Note 3

One may argue that the server should sign outgoing activities using the key of its so called instance actor, i.e. an actor representing the whole server. This sounds even more appropriate in [CSO], as, in this case, the activity is not provided by the client but generated by the server itself.

Random thoughts

In a discussion on the Semantic Web Community Group mailing list a security flow has been reported. However, in my opinion this flow does not concern if the signature is generated client-side or server side.

There are other approaches which should be taken into considerations, for example: