HTTP Message Signatures
Extracted elements (28)
The @authority derived component SHOULD be used instead of signing the Host header field directly. Host is HTTP/1.1-specific, while @authority provides a uniform normalized representation across HTTP versions (HTTP/2 and HTTP/3 convey authority via the :authority pseudo-header).
TLS provides message integrity and authenticity only over a single TLS connection. When paths include TLS-terminating gateways or TLS inspection appliances, TLS cannot guarantee end-to-end integrity between client and application. HTTP Message Signatures address this by providing application-layer signatures detached from transport.
The @request-target derived component maps directly to the HTTP/1.1 request-line target but is more difficult to construct reliably in HTTP/2 and HTTP/3. Its use is NOT RECOMMENDED when versions other than HTTP/1.1 might be in use.
An application or profile of this specification MUST specify at a minimum: required covered component identifiers and signature parameters, expected Structured Field types for covered components, means of retrieving key material, allowable signature algorithms, means of verifying algorithm-key appropriateness, means of determining message component context, and error codes returned on verification failure.
Derived component names MUST start with the '@' character, which differentiates them from HTTP field names (which cannot contain '@'). Processors MUST treat derived component names separately from HTTP field names.
HTTP field component names MUST be the lowercased form of the field name. Multiple field values for the same field MUST be combined into a single value by concatenating with a single comma and space separator. All non-ASCII field values MUST be encoded to ASCII before inclusion in the signature base.
If any covered component cannot be resolved during signature base creation—due to unknown derived component name, missing field, unknown or incompatible parameters, missing Dictionary key, or non-ASCII output—the implementation MUST produce an error immediately and not output a signature base.
The @signature-params component MUST be the last line of every signature base and its identifier MUST NOT appear in the covered components set for any signature. This ensures every signature always covers its own metadata, preventing metadata substitution attacks.
When creating a signature, the signer MUST choose key material appropriate for the algorithm and MUST include @signature-params as the last covered component. Signers SHOULD include message control data (e.g., @method, @authority, @target-uri) and SHOULD include the created signature parameter.
HTTP Message Signatures provide integrity and authenticity but explicitly do not provide confidentiality. Signed messages remain readable by any party that intercepts them. Confidentiality must be provided independently by TLS or other encryption mechanisms.
Signed messages can function as signing oracles: if an attacker can cause a party to sign attacker-chosen content (e.g., by injecting controlled values into covered components), the attacker may be able to forge signatures for chosen messages or extract information about the signing key. Applications must carefully control what components are covered.
The key material identifier conveyed in the keyid signature parameter can be used to track and identify users or systems across requests, even when message content does not reveal identity. Long-lived or widely shared key identifiers increase this risk; applications should consider key rotation and unlinkability requirements.
An HTTP message component identifier consists of a component name (lowercased HTTP field name or a '@'-prefixed derived component name) plus optional parameters: sf (strict Structured Field serialization), key (Dictionary member selector), bs (binary-wrapped encoding), req (pull from request in response context), and tr (use trailer fields). Each component identifier MUST appear only once in a covered components list.
Nine derived components are defined: @method (HTTP method string, case-preserved), @target-uri (full assembled target URI), @authority (normalized host[:port]), @scheme (lowercased URI scheme), @request-target (raw request target, HTTP/1.1 only), @path (absolute path, percent-encoded preserved), @query (full query string including leading '?'), @query-param (individual named query parameter with required name parameter), and @status (3-digit status code for responses only).
Signature parameters are metadata serialized as a parameterized Structured Field Inner List. Defined parameters: created (UNIX timestamp Integer, RECOMMENDED), expires (UNIX timestamp Integer), nonce (random String), alg (algorithm identifier String from the HTTP Signature Algorithms registry), keyid (key identifier String), and tag (application-specific String). Additional parameters may be registered.
Six signature algorithms are defined with string identifiers: rsa-pss-sha512 (RSASSA-PSS with SHA-512), rsa-v1_5-sha256 (RSASSA-PKCS1-v1_5 with SHA-256), hmac-sha256 (HMAC-SHA256), ecdsa-p256-sha256 (ECDSA P-256 with SHA-256), ecdsa-p384-sha384 (ECDSA P-384 with SHA-384), and ed25519 (EdDSA with edwards25519). JWS algorithms may also be mapped.
The Accept-Signature HTTP field allows a sender to request that the recipient apply specified signatures to subsequent messages in the exchange. Its value is a Dictionary in the same format as Signature-Input, specifying required labels, covered components, and parameters. The recipient SHOULD apply the requested signatures if capable.
The req Boolean parameter on a component identifier indicates that the value is derived from the request that triggered a response, not from the response itself. This allows a response signature to bind to request components (e.g., method, path, Content-Digest). The req parameter MUST NOT be used in any component in a signature targeting a request message.
The Signature HTTP field carries the raw signature values. Its value is a Structured Field Dictionary where each key is a signature label matching a Signature-Input entry and each value is a Byte Sequence containing the raw bytes output by the HTTP_SIGN cryptographic primitive.
The Signature-Input HTTP field carries signature metadata for all signatures on a message. Its value is a Structured Field Dictionary where each key is a signature label and each value is an Inner List of covered component identifier Strings with signature parameters (created, keyid, alg, etc.) as parameters on the Inner List.
Four IANA registries are created: 'HTTP Signature Algorithms' (string identifiers like rsa-pss-sha512), 'HTTP Signature Metadata Parameters' (signature parameter names like created/expires/nonce/alg/keyid/tag), 'HTTP Signature Derived Component Names' (names like @method, @authority, @path), and 'HTTP Signature Component Parameters' (field component parameter names like sf, key, bs, req, tr). All use Specification Required policy.
Insufficient coverage is a primary vulnerability: if security-relevant components (method, authority, path, query, Content-Digest) are excluded from covered components, an attacker can modify those parts of the message without invalidating the signature. Applications MUST mandate coverage of all components relevant to their security properties.
Key and algorithm downgrade attacks can occur when the verifier accepts a weaker algorithm than intended or when a key is used with an algorithm for which it was not intended. Applications MUST specify allowed algorithms and ensure that algorithm-key combinations are validated; the alg parameter or key material derivation should be used to prevent substitution.
Multiple signatures on a single message can cause confusion when an attacker substitutes a valid signature from a different context (a different label or different application tag). Applications SHOULD use the tag parameter to scope signatures to specific application contexts and validate that the signature label and tag match expectations.
Signature replay attacks allow a valid signature captured from one context to be reused in a different context. Mitigations include using the created/expires parameters to bound validity windows, including a nonce, covering method/authority/path/query to constrain reuse scope, and binding to message content via the Content-Digest field from [DIGEST].
Signing the Signature or Signature-Input fields of another signature (signing signatures) does not provide transitive coverage of the original covered components and is susceptible to collision and key mixup attacks. Including these fields in response covered components is NOT RECOMMENDED. Applications needing to signal receipt of a signature must specify alternative mechanisms.
This specification does not directly cover HTTP message content (body). Applications that need to bind a signature to message content MUST include the Content-Digest field (defined in [DIGEST], RFC 9530) as a covered component, which provides a cryptographic hash of the message body.
The signature base is an ASCII string with ABNF: signature-base = *( signature-base-line LF ) signature-params-line; signature-base-line = component-identifier ":" SP (derived-component-value / *field-content); signature-params-line = DQUOTE "@signature-params" DQUOTE ":" SP inner-list. Component names are double-quoted String Items with parameters as semicolon-separated list.