When a Mastodon server receives an incoming ActivityPub request from another instance, it must verify that the request truly originated from the claimed actor and was not tampered with in transit. Without this verification, an attacker could forge messages, impersonate users, or inject spam into the federated timeline. Mastodon relies on HTTP Signatures, a standard defined in the IETF HTTP Signatures draft, to authenticate every inter-server request. This walkthrough explains the exact steps Mastodon performs to validate an incoming HTTP Signature, from parsing the header to checking the cryptographic digest.
Key Takeaways: HTTP Signature Verification in Mastodon Federation
- Signature header parsing: Mastodon extracts the keyId, algorithm, headers list, and signature value from the
SignatureHTTP header. - Key retrieval via keyId: The server fetches the actor’s public key from the URL specified in the keyId field, typically stored as a
publicKeyproperty on the actor’s ActivityPub profile. - Signature string construction: Mastodon rebuilds the signed string by concatenating the values of the headers listed in the
headersparameter, separated by newline characters.
Why Mastodon Verifies HTTP Signatures on Incoming Federation Requests
Mastodon instances communicate using the ActivityPub protocol, which relies on HTTP POST requests to deliver activities like Create, Follow, and Like. Each request carries a payload in JSON-LD format. If a server accepted these requests without authentication, any malicious actor could send fake activities that appear to come from a legitimate user on a different instance. HTTP Signatures prevent this by allowing the receiving server to cryptographically verify the sender’s identity.
The verification process hinges on asymmetric cryptography. The sending actor possesses a private key, and the public key is published as part of the actor’s ActivityPub profile, accessible via a URL. Mastodon never stores private keys for remote actors. Instead, it fetches the public key on demand and uses it to verify the signature included in the HTTP header. The entire process is stateless, which makes it scalable across thousands of federated instances.
The Role of the HTTP Signature Header
The Signature header is sent by the originating server with every ActivityPub POST request. It contains several parameters: keyId, algorithm, headers, and signature. The keyId is a URL pointing to the actor’s public key. The algorithm parameter specifies the hashing algorithm, usually rsa-sha256. The headers parameter lists which HTTP headers were included in the signature calculation. The signature value is the base64-encoded result of signing the concatenated header values with the actor’s private key.
Step-by-Step: How Mastodon Verifies an Incoming HTTP Signature
- Extract the Signature header
The receiving Mastodon server reads theSignatureheader from the incoming HTTP request. It parses the comma-separated key-value pairs to extractkeyId,algorithm,headers, andsignature. If the header is missing or malformed, Mastodon rejects the request immediately with a 401 Unauthorized response. - Resolve the actor’s public key from the keyId URL
Mastodon takes thekeyIdvalue, which is a URL likehttps://instance.example/users/alice#main-key. It performs an HTTP GET request to that URL to fetch the actor’s ActivityPub profile. The response must include apublicKeyobject with apublicKeyPemfield containing the RSA public key in PEM format. Mastodon caches this key for a short period to reduce repeated fetches. - Determine which headers were signed
Mastodon reads theheadersparameter from the Signature header. This parameter lists the HTTP header names that the sending server included in the signature calculation, for example:(request-target) host date digest. If theheadersparameter is absent, Mastodon defaults todateonly, but modern Mastodon versions require the(request-target)anddigestheaders for security. - Rebuild the signed string
Mastodon constructs a string by taking the value of each header listed in theheadersparameter, in the same order. For each header, it writes the lowercased header name, a colon, a space, and the header value. Each header-value pair is separated by a newline character. The special pseudo-header(request-target)is replaced with the HTTP method and path, for example:post /inbox. - Decode the signature and verify it against the public key
Mastodon base64-decodes thesignatureparameter to obtain the raw signature bytes. It then uses the public key from step 2 and the algorithm specified in the header to verify the signature against the rebuilt signed string. For RSA-SHA256, Mastodon computes the SHA-256 hash of the signed string and attempts to decrypt the signature with the public key. If the decrypted hash matches the computed hash, the signature is valid. - Check the digest header (if present)
If the request includes aDigestheader, Mastodon computes the SHA-256 hash of the request body and compares it to the value in theDigestheader. This ensures the body was not modified after the signature was created. If the digests do not match, Mastodon rejects the request even if the signature is valid. - Verify the date header freshness
Mastodon checks theDateheader (orOriginal-Dateif configured) to ensure the request was sent within the last 30 seconds. This prevents replay attacks where an attacker captures a valid signed request and sends it again later. If the timestamp is too old or in the future by more than 30 seconds, Mastodon rejects the request.
Common Verification Failures and Their Root Causes
“HTTP Signature Verification Failed” Error in Server Logs
This error appears when the cryptographic verification in step 5 fails. The most common cause is a mismatch between the public key the sending instance advertises and the private key it used to sign the request. This can happen after an admin rotates the server’s key pair but does not update the actor’s publicKey field in the database. The fix is to ensure that the publicKey property on the actor’s ActivityPub profile matches the private key used by the sending application.
“Key Not Found” Error When Fetching the Actor
If the receiving Mastodon cannot fetch the actor’s profile from the keyId URL, it logs a “Key not found” error. This occurs when the sending instance is down, the URL is incorrect, or the actor’s profile does not include a publicKey property. Mastodon retries the fetch up to three times with a delay of one second between attempts. If the fetch still fails, the request is rejected. Administrators should verify that the sending instance’s ActivityPub endpoint is reachable and returns valid JSON-LD.
“Digest Mismatch” Error
A digest mismatch indicates that the request body was modified after the signature was created. This can happen if a proxy or load balancer between the two instances alters the request body, for example by compressing it or stripping whitespace. The fix is to configure the intermediate infrastructure to pass the request body unchanged. Mastodon also supports the Digest header with the SHA-256 algorithm; if the proxy recalculates the digest, it must update the header accordingly.
HTTP Signature Verification: Mastodon vs ActivityPub Specification
| Item | Mastodon Implementation | ActivityPub Specification |
|---|---|---|
| Default signed headers | (request-target) host date digest |
Recommends (request-target) date |
| Required algorithm | RSA-SHA256 only | Supports any algorithm listed in the HTTP Signatures draft |
| Date header tolerance | 30 seconds | No specific tolerance defined |
| Digest verification | Mandatory for POST requests | Optional |
| Key caching | 10 minutes | Not specified |
Mastodon’s implementation is stricter than the ActivityPub specification to improve security. The mandatory digest header and the short date tolerance reduce the attack surface for replay and body tampering attacks. However, this strictness can cause compatibility issues with other fediverse software that implements a looser interpretation of the HTTP Signatures draft.
After reading this walkthrough, you can trace through the verification logic in your own server logs when federation errors occur. The next time you see a “signature verification failed” message, check the keyId URL and the signed headers list first. For advanced debugging, enable verbose logging on your Mastodon instance by setting SINGLE_USER_MODE=false and VERBOSE_FEDERATION=true in the environment configuration to see the exact signed string Mastodon constructed.