Mastodon Federation Signature Verification: HTTP Signature Walkthrough
🔍 WiseChecker

Mastodon Federation Signature Verification: HTTP Signature Walkthrough

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 Signature HTTP 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 publicKey property on the actor’s ActivityPub profile.
  • Signature string construction: Mastodon rebuilds the signed string by concatenating the values of the headers listed in the headers parameter, separated by newline characters.

ADVERTISEMENT

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

  1. Extract the Signature header
    The receiving Mastodon server reads the Signature header from the incoming HTTP request. It parses the comma-separated key-value pairs to extract keyId, algorithm, headers, and signature. If the header is missing or malformed, Mastodon rejects the request immediately with a 401 Unauthorized response.
  2. Resolve the actor’s public key from the keyId URL
    Mastodon takes the keyId value, which is a URL like https://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 a publicKey object with a publicKeyPem field containing the RSA public key in PEM format. Mastodon caches this key for a short period to reduce repeated fetches.
  3. Determine which headers were signed
    Mastodon reads the headers parameter 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 the headers parameter is absent, Mastodon defaults to date only, but modern Mastodon versions require the (request-target) and digest headers for security.
  4. Rebuild the signed string
    Mastodon constructs a string by taking the value of each header listed in the headers parameter, 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.
  5. Decode the signature and verify it against the public key
    Mastodon base64-decodes the signature parameter 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.
  6. Check the digest header (if present)
    If the request includes a Digest header, Mastodon computes the SHA-256 hash of the request body and compares it to the value in the Digest header. 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.
  7. Verify the date header freshness
    Mastodon checks the Date header (or Original-Date if 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.

ADVERTISEMENT

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.

ADVERTISEMENT