-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.html
267 lines (224 loc) · 28.5 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>ActivityPub and HTTP Signatures</title>
<script
src="https://www.w3.org/Tools/respec/respec-w3c"
class="remove"
defer
></script>
<script class="remove">
// All config options at https://respec.org/docs/
var respecConfig = {
specStatus: "CG-DRAFT",
editors: [ { name: "Ryan Barrett", url: "https://snarfed.org/" }, { name: "nightpool", url: "https://github.com/nightpool" } ],
github: "swicg/activitypub-http-signature",
shortName: "apsig",
xref: "web-platform",
group: "socialcg",
};
</script>
</head>
<body>
<br>
<p><em>This document is non-normative.</em></p>
<section id="abstract">
<p>Authentication is not specified by the [[[ActivityPub]]] standard. In practice, the fediverse mostly uses [[[RFC9421]]] to authenticate server-to-server requests, using a relatively consistent profile. This document describes that profile and usage, recommends best practices, and evaluates their success so far.</p>
</section>
<section id="sotd">
</section>
<section>
<h2>Introduction</h2>
<p>[[ActivityPub]] lets people interact on the fediverse without an existing shared trust anchor. That's great! They still need some form of trust model, though, even if the protocol is decentralized. Specifically, they need to authenticate actors who send activities and request objects, and they need to check whether those actors are authorized to send those activities and request those objects.</p>
<p><a href="https://www.w3.org/wiki/SocialCG/ActivityPub/Authentication_Authorization">The (non-normative) ActivityPub primer discusses authentication and authorization in detail</a>, but the standard itself leaves authentication and authorization largely unspecified. It implies that authorization is a "same origin model" (not to be confused with web browsers): an actor can generally only create, update, or delete objects with itself as actor or <code>attributedTo</code>. Some of this is deferred to [[[activitystreams-core]]].</p>
<p>For authentication, in practice, the fediverse currently uses a custodial trust model. Each user has one or more asymmetric keypairs, and their instance (server) is generally trusted to hold their private keys, serve their public keys, generate signatures, and serve objects and send activities on their behalf. Most servers require SSL for server-to-server HTTP connections in order to authenticate server domains.</p>
<p>ActivityPub suggests [[[RFC9421]]] and <a href="https://web.archive.org/web/20170923124140/https://w3c-dvcg.github.io/ld-signatures/">Linked Data Signatures</a> as additional authentication mechanisms. ActivityPub inbox delivery POSTs generally include an HTTP Signature from the sending actor, and for inbox forwarding, sometimes also an LD Signature from the original actor. Object and activity GETs often include an HTTP Signature from the requesting actor.</p>
<p>This document describes the fediverse's current usage of HTTP Signatures for ActivityPub server-to-server requests, recommends best practices, and evaluates their success so far.
<section>
<h3>Non-goals</h3>
<p>Those are broad goals, and this report is limited in scope. Here are a number of non-goals that are intentionally not addressed or prioritized here.</p>
<ul>
<li> Authorization or access control. We mention them here and there, but we don't try to cover them comprehensively.</li>
<li> Reproducing the contents of existing standards like HTTP Signatures itself (any version), PEM/PKCS, SHA-256, [[[activitystreams-core]]], [[[vc-data-integrity]]], HTTP headers, hash algorithms or cipher suites or cryptosystems, etc.</li>
<li> Strict standards compliance at the expense of interoperability.</li>
<li> Providing the strongest security possible, at the expense of interoperability.</li>
<li> Security analysis of HTTP Signatures, ActivityPub, their integration, or any individual implementation.</li>
<li> Nomadic identity.</li>
<li> Fragment handling in ActivityPub or ActivityStreams ids, beyond the minimum needed to resolve <code>keyId</code>s.</li>
<li> ActivityPub client to server, or generating signatures client side.</li>
</ul>
<p>We also don't expect to update this report on an ongoing basis. It describes a single snapshot in time, when it was published.</p>
</section>
<section>
<h3>Comparison with other networks</h3>
<p>Other decentralized social protocols have similar trust models and authentication techniques. Almost are all some combination of SSL and/or per-user asymmetric keypairs. Here are a few examples.</p>
<ul>
<li>The <a href="https://indieweb.org/">IndieWeb</a> ecosystem uses primarily SSL for authentication. Data is always fetched via HTTP GETs, and each user is generally expected to have their own [sub]domain. It also uses [[IndieAuth]], a domain-based OAuth [[RFC6749]] profile, to authenticate users with their own servers, but not for requests between servers themselves.</li>
<li><a href="https://atproto.com/">AT Protocol</a> <a href="https://atproto.com/specs/did">uses DIDs as user identities</a>, specifically <a href="https://github.com/did-method-plc/did-method-plc">did:plc</a> and <a href="https://w3c-ccg.github.io/did-method-web/">did:web</a>. Each user has <a href="https://atproto.com/guides/overview#account-portability">two asymmetric keypairs, a signing key and a recovery key</a>. User data records are signed with one of these keys, usually the signing key, and other services in the network verify those signatures.</li>
<li><a href="https://nostr.com/">Nostr</a> users publish an asymmetric keypair inside their <a href="https://github.com/nostr-protocol/nips/blob/master/01.md">NIP-01</a> user profile event to one or more relays. They sign all other events they publish with their private key.</li>
</ul>
</section>
<section>
<h3>Survey of standards compliance</h3>
<p><a href="https://vpzom.click/">vpzom</a>'s <a href="https://arewehs2019yet.vpzom.click/">Are We HS2019 Yet?</a> web site faithfully tracks HTTP Signature support in popular fediverse server projects over time. As of 2024-03-16, it covers 14 projects, including 8 of the top 10 projects by total registered users.</p>
<p>We also confirmed micro.blog's and the WordPress ActivityPub plugin's HTTP Signature support. Combined with vpzom's survey, these 16 projects together cover 96% of registered users, 95% of MAUs, and 83% of instances in the fediverse. (Based on <a href="https://fedidb.org/software">fedidb.org/software</a> and <a href="https://fedidb.org/software">fediverse.observer/stats</a>.)</p>
<p>We've refrained from duplicating vpzom's full contents, but here are a few highlights.</p>
<ul>
<li>
<p>All 16 projects support HTTP Signatures, both generating and sending them in outbound requests and validating signatures on incoming requests.</p>
</li>
<li>
<p>Based on this data and more, [[[http-signatures]]] aka <em>cavage-12</em> is still the most commonly supported version in the fediverse by far. Not many projects support later versions, especially not projects with large user or instance bases.</p>
</li>
<li>
<p>Very few projects share implementation code. PeerTube and Misskey both use <a href="https://www.npmjs.com/package/@peertube/http-signature">@peertube/http-signature</a>, Pleroma and Mobilizon both use <a href="https://git.pleroma.social/pleroma/elixir-libraries/http_signatures">pleroma/http_signatures</a>, the rest all use their own implementations.</p>
</li>
<li>
<p>All projects except one support the <code>hs2019</code> placeholder algorithm in at least some form. This is a good sign that the fediverse is gradually advancing the version(s) of HTTP Signatures it supports, but getting past cavage-12 will take more effort.</p>
</li>
</ul>
</section>
<section>
<h3>Related work</h3>
<p><a href="https://codeberg.org/aumetra/fep/src/branch/fep-e2ce/fep/e2ce/fep-e2ce.md">FEP-e2ce</a> describes a similar standardized profile for HTTP Signatures in ActivityPub and the fediverse. <a href="https://github.com/swicg/activitypub-http-signature/issues/19">Its authors supported us in writing this report</a>, which we appreciate greatly!</p>
<p><a href="https://codeberg.org/fediverse/fep/src/branch/main/fep/521a/fep-521a.md">FEP-521a</a> describes an alternative way to support multiple keys per actor with <code>Multikey</code>.</p>
<p><a href="https://codeberg.org/fediverse/fep/src/branch/main/fep/ae97/fep-ae97.md">FEP-ae97</a> proposes a way for clients to hold users' keys and attach data integrity proof style signatures to activities.</p>
</section>
</section>
<section>
<h2>Basic usage</h2>
<section>
<h3>How to sign a request</h3>
<p>Here's how to sign an ActivityPub server-to-server HTTP request with a <a href="https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures">cavage-12</a> HTTP Signature:</p>
<ol>
<li>Load the signing actor's private key.
<ol>
<li>If this is a new actor, generate and store a 2048-bit or larger <a href="https://en.wikipedia.org/wiki/RSA_(cryptosystem)">RSA keypair</a>. (Ed25519 is more modern, but not yet widely supported in the fediverse.) Use an existing, mature, widely used cryptography library.</li>
</ol>
</li>
<li>Generate a <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Digest"><code>Digest</code></a> [[RFC3230]] header value. (Most servers should generally only require this for POST requests, so you may be able to omit it for GETs.)
<ol>
<li>Generate the <a href="https://en.wikipedia.org/wiki/SHA-2">SHA-256 hash</a> of the request body.</li>
<li>Base64-encode that hash.</li>
<li>Prefix the base64-encoded hash with the string <code>SHA-256=</code>.</li>
</ol>
</li>
<li>Generate the <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Date"><code>Date</code></a>, <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Host"><code>Host</code></a>, and <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type"><code>Content-Type</code></a> header values.</li>
<li>Use the HTTP request's target URL as the <code>(request-target)</code> pseudo-header.</li>
<li>Follow <a href="https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures#autoid-13">cavage-12 sections 2.3 and 2.4</a> to generate the signature with those five headers and values and the request body, if any.
<ol>
<li>Use <a href="https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures#autoid-38"><code>hs2019</code></a> as the algorithm. This is a generic placeholder string that defers algorithm detection to the <code>keyId</code> public key's metadata.</li>
<li>Make sure that all header names are lower case. Details in cavage-12 section 2.3.</li>
</ol>
</li>
<li>Add the resulting signature string to the request in the <code>Signature</code> header <a href="https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures#autoid-21">as described in cavage-12 section 4</a>.</li>
</ol>
</section>
<section>
<h3>How to verify a signature</h3>
<p>Here's how to verify an incoming request's HTTP Signature, as described in <a href="https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12#autoid-15">cavage-12 section 2.5</a>. If the verification fails, and you require a valid signature for the given request, you should return an HTTP 401 error response.</p>
<ol>
<li>The HTTP Signature string is the value of the HTTP request's <code>Signature</code> header. If that header is not present, the request has no signature.</li>
<li>Extract the <a href="https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12#autoid-6"><code>keyId</code> parameter</a> from the HTTP Signature.</li>
<li>Follow the instructions in [[[#how-to-obtain-a-signature-s-public-key]]] to obtain the public key for that <code>keyId</code>.</li>
<li>Extract the encryption algorithm from the signature. If it's <code>hs2019</code>, assume that means <code>rsa-sha256</code>, as described in [[[#survey-of-standards-compliance]]].
<li>Extract the signed headers from the signature. They should ideally include <code>Date</code>, <code>Host</code>, and <code>Content-Type</code>. They may also include <code>Digest</code> and the <code>(request-target)</code> pseudo-header with the target URL.</li>
<li>Generate the expected request body digest:
<ol>
<li>Generate the <a href="https://en.wikipedia.org/wiki/SHA-2">SHA-256 hash</a> of the request body.</li>
<li>Base64-encode that hash.</li>
<li>Prefix the base64-encoded hash with the string <code>SHA-256=</code>.</li>
</ol>
</li>
<li>Optionally compare this to the request's <code>Digest</code> header. If they don't match, the signature is invalid, and you can optionally return an informative message in the error response.</li>
<li>Compare the request's <code>Date</code> header to the current time. If they differ significantly, the verification fails. (The standards don't give a concrete time window to use for this comparison. In practice, an hour plus a few minutes buffer in either direction may be a good value, to account for both clock skew and differences in time zone/daylight savings time configuration across systems.)</li>
<li>Follow <a href="https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12#autoid-15">cavage-12 section 2.5</a> to check the signature in the <code>Signature</code> header.</li>
<li>If that verification fails, and you're using a locally cached public key, the actor may have rotated their key (see [[[#key-rotation]]]). Go back to step 3, re-fetch the actor and key from their source, and try again.</li>
</ol>
</section>
<section>
<h3>How to obtain a signature's public key</h3>
<p>Here's how to find the public key to use to verify a fediverse HTTP Signature:</p>
<ol>
<li>Extract the signature's <a href="https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12#section-2.1.1"><code>keyId</code> parameter</a>. It should be a URL. If it has a fragment, discard it.</li>
<li>If you cache remote ActivityPub objects locally, look up the object with that id in your local store.</li>
<li>If you don't have it locally, fetch it. (If it's an [[[#instance-actor]]], the remote server generally won't expect or try to verify any signature on your own fetch request.)</li>
<li>If it's an [[[activitystreams-core]]] <a href="https://www.w3.org/TR/activitystreams-core/#actors">actor</a>, continue to step 7.</li>
<li>If it's a raw <a href="https://w3c-ccg.github.io/security-vocab/#Key"><code>Key</code> object</a>, use its <code>controller</code> or <code>owner</code> property as the new key id, jump back to step 2, and repeat. (This is necessary to confirm that the owner actually owns and uses this key.)</li>
<li>Otherwise, the key can't be fetched at this time, and the signature verification fails.</li>
<li>The public key object will be in the actor's <code>publicKey</code> property. If there are multiple values, find the one whose <code>id</code> matches the original <code>keyId</code>.</li>
<li>The <a href="https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail">PEM-encoded</a> public key will be in the key object's <code>publicKeyPem</code> property. This is based on <a href="https://w3c-ccg.github.io/security-vocab/#publicKey">LD Security Vocabulary v1</a>. Use your cryptography library to decode it as a PEM public key. (It may be encoded as <a href="https://datatracker.ietf.org/doc/html/rfc3447">PKCS-1</a>, <a href="https://datatracker.ietf.org/doc/html/rfc5280#appendix-A">X.509 SPKI</a>, or something else; your library should detect its format automatically.)</li>
</ol>
<p>Note that a <a href="https://w3c.github.io/vc-data-integrity/vocab/security/vocabulary.html">newer version of the LD Security Vocabulary</a> (part of <a href="https://w3c.github.io/vc-data-integrity/">Verifiable Credential Data Integrity</a>) removes the <code>publicKey</code> property. <a href="https://codeberg.org/fediverse/fep/src/branch/main/fep/521a/fep-521a.md">FEP-521a</a> is an alternative that supports key objects anywhere in actors, eg in the <code>assertionMethod</code> property, but it's not yet widely supported in the fediverse.</p>
</section>
</section>
<section>
<h2>Additional considerations</h2>
<section>
<h3>Authorized fetch</h3>
<p>Some servers have a feature called <em>authorized fetch</em>, aka secure mode, which requires HTTP Signatures on all HTTP GET requests for objects and activities. This is intended to increase the server's control over who can access its data. <a href="https://docs.joinmastodon.org/admin/config/#authorized_fetch">From Mastodon's documentation</a>:</p>
<blockquote>As a result, through the authentication mechanism and avoiding re-distribution mechanisms that do not have your server in the loop, it becomes possible to enforce who can and cannot retrieve even public content from your server, e.g. servers whose domains you have blocked.</blockquote>
<p>For public posts and data, servers with authorized fetch enabled generally don't enforce any fine grained access control over the actors whose signatures they require. They usually only reject requests from actors on domains that they've blocked at the server level. (This means that the name authorized fetch is maybe partially a misnomer, and something like signed fetch might be more appropriate.)</p>
<p>For non-public data, eg followers-only or mention-only posts aka direct messages, servers generally do check that GET requests are signed by actors who should have access to that data.</p>
</section>
<section>
<h3>Instance actor</h3>
<p>One consequence of [[[#authorized-fetch]]] is that signed fetches can end up in a kind of deadlock or infinite loop. If server a.example fetches <a href="https://b.example/bob">https://b.example/bob</a> with <a href="https://a.example/alice">https://a.example/alice</a>'s signature, and b.example doesn't have alice's public key, it will get it by fetching <a href="https://a.example/alice">https://a.example/alice</a> with <a href="https://b.example/bob's">https://b.example/bob's</a> signature. a.example won't have bob's public key either, so it will again try to fetch <a href="https://b.example/bob">https://b.example/bob</a>, and the cycle will continue.</p>
<p>To prevent this, servers with authorized fetch enabled often use an <a href="https://seb.jambor.dev/posts/understanding-activitypub-part-4-threads/#the-instance-actor">instance actor</a> to sign object fetches. This is generally a "server-level" actor, separate from any normal user, that doesn't require an HTTP Signature to be fetched. This breaks the loop of fetching each actor back and forth to validate their signatures.</p>
</section>
<section>
<h3>Handling <code>Delete</code>s of actors</h3>
<p><code>Delete</code> activities that delete actors can have extra complications. The actor object may already be deleted on the source server, so fetching it might return a 410 or 404 error. Or, the <code>Delete</code> activity may be signed by the remote server's [[[#instance-actor]]] instead of the actor itself.</p>
<p>Here's a process for handling <code>Delete</code> activities that delete actors:</p>
<ol>
<li>Attempt to verify the request's signature by following [[[#how-to-verify-a-signature]]]. If that succeeds, process the <code>Delete</code>.</li>
<li>If the signature's `keyId` doesn't match the <code>Delete</code>'s <code>object</code> or its server's [[[#instance-actor]]], discard the <code>Delete</code> and do not process it.<br>
<em>Note that there is no standard process for identifying a given server's instance actor! If in doubt, do not process the activity.</em></li>
<li>If fetching the `keyId` in [[[#how-to-obtain-a-signature-s-public-key]]] fails with an HTTP 410 error, that means the actor has been deleted. Process the Delete.</li>
<li>If fetching the `keyId` in [[[#how-to-obtain-a-signature-s-public-key]]] fails with an HTTP 404 error, the actor <em>may</em> have been deleted, or something else may have happened. Do not process the <code>Delete</code>.</li>
</ol>
</section>
<section>
<h3>Compatibility with HTTP caching</h3>
<p>HTTP responses are cached in a wide variety of ways across the web. HTTP Signatures in ActivityPub requests can affect the resulting responses, so clients and servers both need to take signatures account when interacting with caches.</p>
<p>The main thing ActivityPub servers need to do is include <code>Signature</code> in their <code>Vary</code> header for responses that depend on request signatures, eg if they require [[[#authorized-fetch]]] and would return a 4xx error if a signature is missing or invalid. This prevents valid responses from being cached and returned to other future requests with missing or invalid signatures.</p>
<p>(This is similar to including <code>Content-Type</code> in the <code>Vary</code> response header for URLs that can return either user-facing HTML or [[[activitystreams-core]]] JSON, depending on content negotiation.)</p>
<p>One downside of this is that ActivityPub objects from servers that require authorized fetch generally can't be cached. HTTP Signatures include timestamps via the <code>Date</code> header, and are often generated by different private keys, so they'll almost never be the same across requests. This is an unfortunate side effect, but necessary for servers that want to control access based on the requester's identity.</p>
</section>
<section>
<h3>How to upgrade supported versions</h3>
<p>The HTTP Signatures standard has made a few backward-incompatible changes on its path to becoming a full Proposed Standard, [[RFC9421]]. Many fediverse servers currently handle older versions of the standard and aren't yet compatible with the final version. Here's advice on how to implement HTTP Signatures so as to be compatible with as many different servers as possible.</p>
<p>The primary technique we recommend is <em>double-knocking</em>. First, try generating or verifying an HTTP Signature with one version, ideally (but not necessarily) the latest. If the remote server rejects that signature, eg with an HTTP 401 response, or the incoming signature doesn't verify, try with another version. Repeat until a signature passes or you've tried all supported versions.</p>
<p>(Many fediverse servers do process incoming activities asynchronously, <a href="https://socialhub.activitypub.rocks/t/report-errors-in-server-processing/3006/13">but they generally still <em>verify signatures</em> synchronously</a>, so double knocking is still viable when delivering activities to remote inboxes.)</p>
<p>Here's a list of ways to check for different versions, in descending order:</p>
<ul>
<li>Does the request have the <a href="https://datatracker.ietf.org/doc/html/rfc9421#name-the-signature-input-http-fi"><code>Signature-Input</code> HTTP header</a>? This was only added in the later versions of the standard, notably after <a href="https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12"><code>cavage-12</code></a>. <a href="https://datatracker.ietf.org/doc/html/rfc9421#appendix-A">The RFC itself advises this in its appendix on backward compatibility.</a> If <code>Signature-Input</code> is present, and the signature fails, try removing it and using <code>cavage-12</code> instead.</li>
<li>Does the signature use the <code>hs2019</code> placeholder algorithm? This was added in <code>cavage-12</code>, then removed again in later versions. <a href="https://datatracker.ietf.org/doc/html/rfc9421#iana-hsa-contents">It's not present in the final proposed standard.</a> If the signature uses <code>hs2019</code> and fails, try again with <code>rsa-sha256</code>.</li>
<li>Does the signature include the <code>(request-target)</code>, <code>(created)</code>, or <code>(expires)</code> pseudo-headers? If so, and the signature fails, try replacing them with the <code>Date</code> and/or <code>Host</code> headers.</li>
</ul>
</section>
<section>
<h3>Key rotation</h3>
<p>Keys don't always stay the same forever. Changing an actor's key is called <em>key rotation</em>, and can be a good idea to improve or maintain security in a number of situations. If a private key is leaked or compromised, you should immediately stop using it and switch to a new key instead. You can also do this proactively, regularly, in case of a leak or compromise that you haven't discovered yet. Or the key may have been lost, and can't be restored from backup.</p>
<p><a href="https://socialhub.activitypub.rocks/t/key-rotation-notification/562">There are two common types of key rotation in the fediverse</a>: <em>signed</em>, where you send an <code>Update</code> activity for the actor with the new key to all followers, and <em>blind</em>, where you don't, for plausible deniability of past activities.</p>
<p>[[[#how-to-verify-a-signature]]] step 10 mentions how to handle key rotation when you're verifying a signature. RFC9421 discusses key rotation briefly in <a href="https://datatracker.ietf.org/doc/html/rfc9421#section-7.3.2"><em>7.3.2 Key theft</em></a> and <a href="https://datatracker.ietf.org/doc/html/rfc9421#section-8.1"><em>8.1. Identification through Keys</em></a>.</p>
</section>
</section>
<section>
<h2>So, what's the verdict?</h2>
<p>The ActivityPub standard briefly mentions authentication (and authorization), but omits specific needs or use cases for them. Over time, two clear uses for authentication in the fediverse have emerged.</p>
<p>The first is proving and verifying that a given user created a given piece of data or performed a given action. This is the classic attribution problem in identity-based networks. (Note that this is separate from authorization or access control, ie determining whether a given user is <em>allowed</em> to access a given piece of data or perform a given action.)</p>
<p>At a baseline level, this works. HTTP Signatures attached to ActivityPub inbox delivery requests can effectively verify the actor - or at least the server - who sent them. However, these signatures are ephemeral. They only authenticate HTTP requests, not long-lived data. We'd often like to authenticate objects and activities outside of inbox delivery requests, eg during <a href="https://www.w3.org/TR/activitypub/#inbox-forwarding">inbox forwarding</a>, or after they were initially created. HTTP Signatures can't do this.</p>
<p>Some fediverse projects like Mastodon use <a href="https://web.archive.org/web/20170923124140/https://w3c-dvcg.github.io/ld-signatures/">LD Signatures 1.0</a> for those purposes instead, which works, but isn't widely supported. Even Mastodon's support itself is limited and discouraged. <a href="https://docs.joinmastodon.org/spec/security/#ld">From their docs</a>:</p>
<blockquote>
Mastodon’s current implementation of LD Signatures is outdated...Furthermore, the LD Signatures specification as a whole has been superseded by [[[vc-data-integrity]]], which is largely incompatible with the earlier LD Signature spec. For this reason, it is not advised to implement support for LD Signatures.
</blockquote>
<p>The second use case for authentication is access control, specifically whether to serve an ActivityPub GET request for a given object or activity. Fediverse users and servers routinely block other remote users and servers, and require [[[#authorized-fetch]]] via HTTP Signatures to identify the remote user making the request to determine whether they're blocked.</p>
<p>Fediverse servers prevent <em>interactions</em> from blocked users/servers via the first use case above: they use HTTP Signatures to identify the remote user, then check if they're blocked. This works, more or less.</p>
<p>As for <em>controlling access</em> to non-public data, eg direct messages and followers-only posts, those are only delivered to the intended recipients' servers, which are expected to only serve them to authorized users. This matches the fediverse's server-centric security model.</p>
<p>Otherwise, as a mechanism for controlling access to <em>public</em> data, HTTP Signatures are only minimally effective, if at all. This isn't a criticism as much as an unavoidable reality. Fediverse servers generally serve public data over the web freely, for anyone to see without logging in or fetching via ActivityPub or HTTP Signature. There's no obvious way to serve public data to anonymous, unauthenticated users, and still block access to specific people. Beyond that, we've seen <a href="https://wedistribute.org/2023/12/authorized-fetch-circumvented/">techniques that circumvent authorized fetch</a> by laundering requests from blocked servers with signatures from other "clean" (non-blocked) server domains.</p>
<p>The conclusion seems to be that HTTP Signatures do serve real use cases in the fediverse, to some degree, but not well, and they're not a solid basis for comprehensive authentication or authorization.</p>
</section>
</body>
</html>