Bind HTTP messages with HTTP Message Signatures

HTTP Message Signatures bind selected HTTP fields and derived components to a key, but applications must define coverage, trust, and freshness.

  • standards updates
  • message signatures
  • integrity
  • authentication
  • structured fields

A signature covers a chosen view of a message

HTTP Message Signatures let a signer bind selected HTTP components to cryptographic key material. A verifier can detect changes to those covered components and decide whether the signing key is trusted for the application.

Coverage is explicit. A signature can include ordinary fields such as Content-Type and derived components such as:

  • @method
  • @target-uri, @authority, @path, and @query
  • @status

Signature parameters such as created, expires, keyid, and nonce are serialized in the mandatory @signature-params line of the signature base. They describe the signature rather than acting as another derived message component.

An uncovered component remains changeable without invalidating the signature. “The request has a valid signature” is therefore incomplete. The important question is whether it covers every component needed for the decision the application is about to make.

Read Signature-Input and Signature together

RFC 9421 defines two Structured Field Dictionaries:

  • Signature-Input names the covered components and signature parameters.
  • Signature carries the resulting signature bytes.

Each signature has a label that must appear in both fields.

POST /orders HTTP/1.1
Host: api.example.invalid
Content-Type: application/json
Content-Digest: sha-256=:ZmFrZS1kaWdlc3Q=:
Signature-Input: sig1=("@method" "@target-uri" "content-type" "content-digest");created=1784150400;keyid="orders-key"
Signature: sig1=:ZmFrZS1zaWduYXR1cmU=:
Content-Length: 13

{"item":"42"}

Expected result: a parser can match the sig1 entries and identify four covered components plus created and keyid metadata. The short byte sequences are illustrative placeholders; cryptographic verification and digest validation will fail unless they are replaced with values calculated from the real message and trusted key.

The order of covered components is preserved in the Inner List. The serialized signature parameters also become part of the signature base, so a verifier must reconstruct them through the RFC algorithms rather than a custom string concatenation.

Define coverage from the application decision

A payment request, webhook, internal gateway assertion, and signed response need different coverage. Begin with the security decision:

  • If routing matters, cover the method and target components.
  • If the authority matters, cover @authority or the complete target URI.
  • If response success or failure matters, cover @status.
  • If representation metadata matters, cover fields such as Content-Type.
  • If message content matters, cover a digest field and verify that digest.

Covering too little permits substitution. Covering unstable or routinely transformed fields can make valid messages unverifiable. Define a profile for each application rather than accepting any technically valid combination.

The verifier must enforce that profile after cryptographic validation. It should reject a signature that omits a required component, uses an unacceptable algorithm, names an untrusted key, or falls outside its freshness policy.

Protect content through a digest

RFC 9421 does not directly place arbitrary message content in the signature base. To bind content, calculate Content-Digest or another application-approved integrity field, include that field in the covered components, and perform two checks:

  1. Verify the HTTP message signature over the covered digest field value.
  2. Recalculate the digest over the received content and compare the bytes.

Skipping the second check lets changed content accompany the original signed digest value. Skipping the first check lets an attacker replace both content and digest.

Content coding is part of this contract. If an intermediary recompresses a message, the content digest changes and the original signature covering it no longer verifies. A transforming intermediary needs an explicit role: preserve the signed bytes, remove an invalidated signature, or apply a new signature under its own trusted identity.

Establish key trust outside the field

A valid mathematical signature does not prove that the correct party signed the message. The verifier must associate keyid, the chosen algorithm, and key material with an identity and authorization that are valid for this particular message.

RFC 9421 supplies the message-signature mechanism, not a universal key distribution or authorization system. An application might use preconfigured keys, a certificate-based system, or another authenticated key-discovery protocol. Whatever mechanism is chosen needs to define:

  • Which signer identities are trusted for which resources and actions.
  • Which algorithms and key parameters are acceptable.
  • How keys are rotated, revoked, and separated between environments.
  • What happens when key discovery is unavailable.
  • Whether an intermediary is signing for itself or attesting to an upstream identity.

Never accept an attacker-supplied key merely because it verifies the attacker-supplied signature.

Add freshness and replay policy

A captured valid signed request can remain valid cryptographically. Preventing harmful replay is an application responsibility.

Signature parameters can carry values such as creation time, expiration time, and a nonce. The application decides which are required, how much clock skew is accepted, how nonces are issued and retained, and whether an operation can be repeated safely.

Cover the method and target when replaying the same signed material at another resource would be dangerous. For state-changing operations, combine signature freshness with application-level duplicate detection or idempotency policy. A timestamp alone does not prevent several replays inside its accepted window.

Account for intermediaries

HTTP fields can be combined, reordered, normalized, added, or removed along a path. Content can be compressed or transformed. A signature survives only when the covered components reconstruct to the same canonical values at the verifier.

Prefer one field instance for a covered field where practical, especially for list-based values. Test the actual gateway, CDN, reverse proxy, framework, and application parser together. A signature that works in a direct unit test can fail after a legitimate intermediary transformation.

An intermediary can also add its own signature with a different label. Multiple signatures do not automatically form a trust chain; the application needs to define what each signer attests and which signatures are required.

Keep TLS in the design

Message signatures do not provide confidentiality. They also do not replace server authentication, traffic analysis protections, or the other channel properties supplied by TLS.

TLS protects each secure connection. Message signatures can add integrity and authenticity for selected components across a path containing multiple TLS terminations or bind an application-specific key to a message. Use both when the threat model requires both channel security and message-level evidence.

Verification checklist

For every accepted signature:

  1. Parse Signature-Input and Signature as Structured Dictionaries.
  2. Require matching, unique labels and valid signature parameters.
  3. Reconstruct the signature base with the RFC 9421 algorithms.
  4. Resolve a trusted key and an allowed algorithm for this exact context.
  5. Verify cryptographically.
  6. Enforce required component coverage and freshness policy.
  7. Recalculate any covered content digest against the received bytes.
  8. Apply replay and authorization checks before acting.
  9. Fail closed when any required step is unavailable or ambiguous.

The result is not a generic “signed HTTP” flag. It is evidence that one trusted key covered one precisely defined set of message components under one application policy.

Primary sources