In order to allow signers and verifiers to establish which components are covered by a signature, this document defines component identifiers for components covered by an HTTP message signature, a set of rules for deriving and canonicalizing the values associated with these component identifiers from the HTTP message, and the means for combining these canonicalized values into a signature base.
The signature context for deriving these values
MUST be accessible to both the signer and the verifier of the message. The context
MUST be the same across all components in a given signature. For example, it would be an error to use the raw query string for the
@query derived component but combined query and form parameters for the
@query-param derived component. For more considerations regarding the message component context, see
Section 7.4.3.
A component identifier is composed of a component name and any parameters associated with that name. Each component name is either an HTTP field name (
Section 2.1) or a registered derived component name (
Section 2.2). The possible parameters for a component identifier are dependent on the component identifier. The "HTTP Signature Component Parameters" registry, which catalogs all possible parameters, is defined in
Section 6.5.
Within a single list of covered components, each component identifier
MUST occur only once. One component identifier is distinct from another if the component name differs or if any of the parameters differ for the same component name. Multiple component identifiers having the same component name
MAY be included if they have parameters that make them distinct, such as
"foo";bar and
"foo";baz. The order of parameters
MUST be preserved when processing a component identifier (such as when parsing during verification), but the order of parameters is not significant when comparing two component identifiers for equality checks. That is to say,
"foo";bar;baz cannot be in the same message as
"foo";baz;bar, since these two component identifiers are equivalent, but a system processing one form is not allowed to transform it into the other form.
The component value associated with a component identifier is defined by the identifier itself. Component values
MUST NOT contain newline (
\n) characters. Some HTTP message components can undergo transformations that change the bitwise value without altering the meaning of the component's value (for example, when combining field values). Message component values therefore need to be canonicalized before they are signed, to ensure that a signature can be verified despite such intermediary transformations. This document defines rules for each component identifier that transform the identifier's associated component value into such a canonical form.
The following sections define component identifier names, their parameters, their associated values, and the canonicalization rules for their values. The method for combining message components into the signature base is defined in
Section 2.5.
The component name for an HTTP field is the lowercased form of its field name as defined in
Section 5.1 of [
HTTP]. While HTTP field names are case insensitive, implementations
MUST use lowercased field names (e.g.,
content-type,
date,
etag) when using them as component names.
The component value for an HTTP field is the field value for the named field as defined in
Section 5.5 of [
HTTP]. The field value
MUST be taken from the named header field of the target message unless this behavior is overridden by additional parameters and rules, such as the
req and
tr flags, below. For most fields, the field value is an ASCII string as recommended by [
HTTP], and the component value is exactly that string. Other encodings could exist in some implementations, and all non-ASCII field values
MUST be encoded to ASCII before being added to the signature base. The
bs parameter, as described in
Section 2.1.3, provides a method for wrapping such problematic field values.
Unless overridden by additional parameters and rules, HTTP field values
MUST be combined into a single value as defined in
Section 5.2 of [
HTTP] to create the component value. Specifically, HTTP fields sent as multiple fields
MUST be combined by concatenating the values using a single comma and a single space as a separator ("," + " "). Note that intermediaries are allowed to combine values of HTTP fields with any amount of whitespace between the commas, and if this behavior is not accounted for by the verifier, the signature can fail, since the signer and verifier will see a different component value in their respective signature bases. For robustness, it is
RECOMMENDED that signed messages include only a single instance of any field covered under the signature, particularly with the value for any list-based fields serialized using the algorithm below. This approach increases the chances of the field value remaining untouched through intermediaries. Where that approach is not possible and multiple instances of a field need to be sent separately, it is
RECOMMENDED that signers and verifiers process any list-based fields taking all individual field values and combining them based on the strict algorithm below, to counter possible intermediary behavior. When the field in question is a Structured Field of type List or Dictionary, this effect can be accomplished more directly by requiring the strict Structured Field serialization of the field value, as described in
Section 2.1.1.
Note that some HTTP fields, such as Set-Cookie [
COOKIE], do not follow a syntax that allows for the combination of field values in this manner (such that the combined output is unambiguous from multiple inputs). Even though the component value is never parsed by the message signature process and is used only as part of the signature base (
Section 2.5), caution needs to be taken when including such fields in signatures, since the combined value could be ambiguous. The
bs parameter, as described in
Section 2.1.3, provides a method for wrapping such problematic fields. See
Section 7.5.6 for more discussion regarding this issue.
If the correctly combined value is not directly available for a given field by an implementation, the following algorithm will produce canonicalized results for list-based fields:
-
Create an ordered list of the field values of each instance of the field in the message, in the order they occur (or will occur) in the message.
-
Strip leading and trailing whitespace from each item in the list. Note that since HTTP field values are not allowed to contain leading and trailing whitespace, this would be a no-op in a compliant implementation.
-
Remove any obsolete line folding within the line, and replace it with a single space (" "), as discussed in Section 5.2 of [HTTP/1.1]. Note that this behavior is specific to HTTP/1.1 and does not apply to other versions of the HTTP specification, which do not allow internal line folding.
-
Concatenate the list of values with a single comma (",") and a single space (" ") between each item.
The resulting string is the component value for the field.
Note that some HTTP fields have values with multiple valid serializations that have equivalent semantics, such as allowing case-insensitive values that intermediaries could change. Applications signing and processing such fields
MUST consider how to handle the values of such fields to ensure that the signer and verifier can derive the same value, as discussed in
Section 7.5.2.
The following are non-normative examples of component values for header fields, given the following example HTTP message fragment:
Host: www.example.com
Date: Tue, 20 Apr 2021 02:07:56 GMT
X-OWS-Header: Leading and trailing whitespace.
X-Obs-Fold-Header: Obsolete
line folding.
Cache-Control: max-age=60
Cache-Control: must-revalidate
Example-Dict: a=1, b=2;x=1;y=2, c=(a b c)
The following example shows the component values for these example header fields, presented using the signature base format defined in
Section 2.5:
"host": www.example.com
"date": Tue, 20 Apr 2021 02:07:56 GMT
"x-ows-header": Leading and trailing whitespace.
"x-obs-fold-header": Obsolete line folding.
"cache-control": max-age=60, must-revalidate
"example-dict": a=1, b=2;x=1;y=2, c=(a b c)
Empty HTTP fields can also be signed when present in a message. The canonicalized value is the empty string. This means that the following empty header field, with (SP) indicating a single trailing space character before the empty field value:
is serialized by the
Section 2.5 with an empty string value following the colon and space added after the component identifier.
Any HTTP field component identifiers
MAY have the following parameters in specific circumstances, each described in detail in their own sections:
-
sf
-
A Boolean flag indicating that the component value is serialized using strict encoding of the Structured Field value (Section 2.1.1).
-
key
-
A String parameter used to select a single member value from a Dictionary Structured Field (Section 2.1.2).
-
bs
-
A Boolean flag indicating that individual field values are encoded using Byte Sequence data structures before being combined into the component value (Section 2.1.3).
-
req
-
A Boolean flag for signed responses indicating that the component value is derived from the request that triggered this response message and not from the response message directly. Note that this parameter can also be applied to any derived component identifiers that target the request (Section 2.4).
-
tr
-
A Boolean flag indicating that the field value is taken from the trailers of the message as defined in Section 6.5 of [HTTP]. If this flag is absent, the field value is taken from the header fields of the message as defined in Section 6.3 of [HTTP] (Section 2.1.4).
Multiple parameters
MAY be specified together, though some combinations are redundant or incompatible. For example, the
sf parameter's functionality is already covered when the
key parameter is used on a Dictionary item, since
key requires strict serialization of the value. The
bs parameter, which requires the raw bytes of the field values from the message, is not compatible with the use of the
sf or
key parameters, which require the parsed data structures of the field values after combination.
Additional parameters can be defined in the "HTTP Signature Component Parameters" registry established in
Section 6.5.
If the value of an HTTP field is known by the application to be a Structured Field type (as defined in [
STRUCTURED-FIELDS] or its extensions or updates) and the expected type of the Structured Field is known, the signer
MAY include the
sf parameter in the component identifier. If this parameter is included with a component identifier, the HTTP field value
MUST be serialized using the formal serialization rules specified in
Section 4 of [
STRUCTURED-FIELDS] (or the applicable formal serialization section of its extensions or updates) applicable to the type of the HTTP field. Note that this process will replace any optional internal whitespace with a single space character, among other potential transformations of the value.
If multiple field values occur within a message, these values
MUST be combined into a single List or Dictionary structure before serialization.
If the application does not know the type of the field or does not know how to serialize the type of the field, the use of this flag will produce an error. As a consequence, the signer can only reliably sign fields using this flag when the verifier's system knows the type as well.
For example, the following Dictionary field is a valid serialization:
Example-Dict: a=1, b=2;x=1;y=2, c=(a b c)
If included in the signature base without parameters, its value would be:
"example-dict": a=1, b=2;x=1;y=2, c=(a b c)
However, if the
sf parameter is added, the value is reserialized as follows:
"example-dict";sf: a=1, b=2;x=1;y=2, c=(a b c)
The resulting string is used as the component value; see
Section 2.1.
If a given field is known by the application to be a Dictionary Structured Field, an individual member in the value of that Dictionary is identified by using the parameter
key and the Dictionary member key as a String value.
If multiple field values occur within a message, these values
MUST be combined into a single Dictionary structure before serialization.
An individual member value of a Dictionary Structured Field is canonicalized by applying the serialization algorithm described in
Section 4.1.2 of [
STRUCTURED-FIELDS] on the
member_value and its parameters, not including the Dictionary key itself. Specifically, the value is serialized as an Item or Inner List (the two possible values of a Dictionary member), with all parameters and possible subfields serialized using the strict serialization rules defined in
Section 4 of [
STRUCTURED-FIELDS] (or the applicable section of its extensions or updates).
Each parameterized key for a given field
MUST NOT appear more than once in the signature base. Parameterized keys
MAY appear in any order in the signature base, regardless of the order they occur in the source Dictionary.
If a Dictionary key is named as a covered component but it does not occur in the Dictionary, this
MUST cause an error in the signature base generation.
The following are non-normative examples of canonicalized values for Dictionary Structured Field members, given the following example header field, whose value is known by the application to be a Dictionary:
Example-Dict: a=1, b=2;x=1;y=2, c=(a b c), d
The following example shows canonicalized values for different component identifiers of this field, presented using the signature base format discussed in
Section 2.5:
"example-dict";key="a": 1
"example-dict";key="d": ?1
"example-dict";key="b": 2;x=1;y=2
"example-dict";key="c": (a b c)
Note that the value for
key="c" has been reserialized according to the strict
member_value algorithm, and the value for
key="d" has been serialized as a Boolean value.
If the value of the HTTP field in question is known by the application to cause problems with serialization, particularly with the combination of multiple values into a single line as discussed in
Section 7.5.6, the signer
SHOULD include the
bs parameter in a component identifier to indicate that the values of the field need to be wrapped as binary structures before being combined.
If this parameter is included with a component identifier, the component value
MUST be calculated using the following algorithm:
-
Let the input be the ordered set of values for a field, in the order they appear in the message.
-
Create an empty List for accumulating processed field values.
-
For each field value in the set:
- 3.1.
- Strip leading and trailing whitespace from the field value. Note that since HTTP field values are not allowed to contain leading and trailing whitespace, this would be a no-op in a compliant implementation.
- 3.2.
- Remove any obsolete line folding within the line, and replace it with a single space (" "), as discussed in Section 5.2 of [HTTP/1.1]. Note that this behavior is specific to [HTTP/1.1] and does not apply to other versions of the HTTP specification.
- 3.3.
- Encode the bytes of the resulting field value as a Byte Sequence. Note that most fields are restricted to ASCII characters, but other octets could be included in the value in some implementations.
- 3.4.
- Add the Byte Sequence to the List accumulator.
-
The intermediate result is a List of Byte Sequence values.
-
Follow the strict serialization of a List as described in Section 4.1.1 of [STRUCTURED-FIELDS], and return this output.
For example, the following field with internal commas prevents the distinct field values from being safely combined:
Example-Header: value, with, lots
Example-Header: of, commas
In our example, the same field can be sent with a semantically different single value:
Example-Header: value, with, lots, of, commas
Both of these versions are treated differently by the application. However, if included in the signature base without parameters, the component value would be the same in both cases:
"example-header": value, with, lots, of, commas
However, if the
bs parameter is added, the two separate instances are encoded and serialized as follows:
"example-header";bs: :dmFsdWUsIHdpdGgsIGxvdHM=:, :b2YsIGNvbW1hcw==:
For the single-instance field above, the encoding with the
bs parameter is:
"example-header";bs: :dmFsdWUsIHdpdGgsIGxvdHMsIG9mLCBjb21tYXM=:
This component value is distinct from the multiple-instance field above, preventing a collision that could potentially be exploited.
If the signer wants to include a trailer field in the signature, the signer
MUST include the
tr Boolean parameter to indicate that the value
MUST be taken from the trailer fields and not from the header fields.
For example, given the following message:
HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked
Trailer: Expires
4
HTTP
7
Message
a
Signatures
0
Expires: Wed, 9 Nov 2022 07:28:00 GMT
The signer decides to add both the Trailer header field and the Expires trailer field to the signature base, along with the status code derived component:
"@status": 200
"trailer": Expires
"expires";tr: Wed, 9 Nov 2022 07:28:00 GMT
If a field is available as both a header and a trailer in a message, both values
MAY be signed, but the values
MUST be signed separately. The values of header fields and trailer fields of the same name
MUST NOT be combined for purposes of the signature.
Since trailer fields could be merged into the header fields or dropped entirely by intermediaries as per
Section 6.5.1 of [
HTTP], it is
NOT RECOMMENDED to include trailers in the signature unless the signer knows that the verifier will have access to the values of the trailers as sent.
In addition to HTTP fields, there are a number of different components that can be derived from the control data, signature context, or other aspects of the HTTP message being signed. Such derived components can be included in the signature base by defining a component name, possible parameters, message targets, and the derivation method for its component value.
Derived component names
MUST start with the "at" (
@) character. This differentiates derived component names from HTTP field names, which cannot contain the
@ character as per
Section 5.1 of [
HTTP]. Processors of HTTP message signatures
MUST treat derived component names separately from field names, as discussed in
Section 7.5.1.
This specification defines the following derived components:
-
@method
-
The method used for a request (Section 2.2.1).
-
@target-uri
-
The full target URI for a request (Section 2.2.2).
-
@authority
-
The authority of the target URI for a request (Section 2.2.3).
-
@scheme
-
The scheme of the target URI for a request (Section 2.2.4).
-
@request-target
-
The request target (Section 2.2.5).
-
@path
-
The absolute path portion of the target URI for a request (Section 2.2.6).
-
@query
-
The query portion of the target URI for a request (Section 2.2.7).
-
@query-param
-
A parsed and encoded query parameter of the target URI for a request (Section 2.2.8).
-
@status
-
The status code for a response (Section 2.2.9).
Additional derived component names are defined in the
Section 6.4.
Derived component values are taken from the context of the target message for the signature. This context includes information about the message itself, such as its control data, as well as any additional state and context held by the signer or verifier. In particular, when signing a response, the signer can include any derived components from the originating request by using the
Section 2.4.
-
request:
-
Values derived from, and results applied to, an HTTP request message as described in Section 3.4 of [HTTP]. If the target message of the signature is a response, derived components that target request messages can be included by using the req parameter as defined in Section 2.4.
-
response:
-
Values derived from, and results applied to, an HTTP response message as described in Section 3.4 of [HTTP].
-
request, response:
-
Values derived from, and results applied to, either a request message or a response message.
A derived component definition
MUST define all target message types to which it can be applied.
Derived component values
MUST be limited to printable characters and spaces and
MUST NOT contain any newline characters. Derived component values
MUST NOT start or end with whitespace characters.
The
@method derived component refers to the HTTP method of a request message. The component value is canonicalized by taking the value of the method as a string. Note that the method name is case sensitive as per [
HTTP],
Section 9.1. While conventionally standardized method names are uppercase [
ASCII], no transformation to the input method value's case is performed.
For example, the following request message:
POST /path?param=value HTTP/1.1
Host: www.example.com
would result in the following
@method component value:
and the following signature base line:
The
@target-uri derived component refers to the target URI of a request message. The component value is the target URI of the request ([
HTTP],
Section 7.1), assembled from all available URI components, including the authority.
For example, the following message sent over HTTPS:
POST /path?param=value HTTP/1.1
Host: www.example.com
would result in the following
@target-uri component value:
https://www.example.com/path?param=value
and the following signature base line:
"@target-uri": https://www.example.com/path?param=value
The
@authority derived component refers to the authority component of the target URI of the HTTP request message, as defined in [
HTTP],
Section 7.2. In HTTP/1.1, this is usually conveyed using the Host header field, while in HTTP/2 and HTTP/3 it is conveyed using the :authority pseudo-header. The value is the fully qualified authority component of the request, comprised of the host and, optionally, port of the request target, as a string. The component value
MUST be normalized according to the rules provided in [
HTTP],
Section 4.2.3. Namely, the hostname is normalized to lowercase, and the default port is omitted.
For example, the following request message:
POST /path?param=value HTTP/1.1
Host: www.example.com
would result in the following
@authority component value:
and the following signature base line:
"@authority": www.example.com
The
@authority derived component
SHOULD be used instead of signing the Host header field directly. See
Section 7.2.4.
The
@scheme derived component refers to the scheme of the target URL of the HTTP request message. The component value is the scheme as a lowercase string as defined in [
HTTP],
Section 4.2. While the scheme itself is case insensitive, it
MUST be normalized to lowercase for inclusion in the signature base.
For example, the following request message sent over plain HTTP:
POST /path?param=value HTTP/1.1
Host: www.example.com
would result in the following
@scheme component value:
and the following signature base line:
The
@request-target derived component refers to the full request target of the HTTP request message, as defined in [
HTTP],
Section 7.1. The component value of the request target can take different forms, depending on the type of request, as described below.
For HTTP/1.1, the component value is equivalent to the request target portion of the request line. However, this value is more difficult to reliably construct in other versions of HTTP. Therefore, it is
NOT RECOMMENDED that this component be used when versions of HTTP other than 1.1 might be in use.
The origin form value is a combination of the absolute path and query components of the request URL.
For example, the following request message:
POST /path?param=value HTTP/1.1
Host: www.example.com
would result in the following
@request-target component value:
and the following signature base line:
"@request-target": /path?param=value
The following request to an HTTP proxy with the absolute-form value, containing the fully qualified target URI:
GET https://www.example.com/path?param=value HTTP/1.1
would result in the following
@request-target component value:
https://www.example.com/path?param=value
and the following signature base line:
"@request-target": https://www.example.com/path?param=value
The following CONNECT request with an authority-form value, containing the host and port of the target:
CONNECT www.example.com:80 HTTP/1.1
Host: www.example.com
would result in the following
@request-target component value:
and the following signature base line:
"@request-target": www.example.com:80
The following OPTIONS request message with the asterisk-form value, containing a single asterisk (
*) character:
OPTIONS * HTTP/1.1
Host: www.example.com
would result in the following
@request-target component value:
and the following signature base line:
The
@path derived component refers to the target path of the HTTP request message. The component value is the absolute path of the request target defined by [
URI], with no query component and no trailing question mark (
?) character. The value is normalized according to the rules provided in [
HTTP],
Section 4.2.3. Namely, an empty path string is normalized as a single slash (
/) character. Path components are represented by their values before decoding any percent-encoded octets, as described in the simple string comparison rules provided in
Section 6.2.1 of [
URI].
For example, the following request message:
GET /path?param=value HTTP/1.1
Host: www.example.com
would result in the following
@path component value:
and the following signature base line:
The
@query derived component refers to the query component of the HTTP request message. The component value is the entire normalized query string defined by [
URI], including the leading
? character. The value is read using the simple string comparison rules provided in
Section 6.2.1 of [
URI]. Namely, percent-encoded octets are not decoded.
For example, the following request message:
GET /path?param=value&foo=bar&baz=bat%2Dman HTTP/1.1
Host: www.example.com
would result in the following
@query component value:
?param=value&foo=bar&baz=bat%2Dman
and the following signature base line:
"@query": ?param=value&foo=bar&baz=bat%2Dman
The following request message:
POST /path?queryString HTTP/1.1
Host: www.example.com
would result in the following
@query component value:
and the following signature base line:
Just like including an empty path component, the signer can include an empty query component to indicate that this component is not used in the message. If the query string is absent from the request message, the component value is the leading
? character alone:
resulting in the following signature base line:
If the query portion of a request target URI uses HTML form parameters in the format defined in Section [
HTMLURL] of [
HTMLURL], the
@query-param derived component allows addressing of these individual query parameters. The query parameters
MUST be parsed according to Section [
HTMLURL] of [
HTMLURL], resulting in a list of (
nameString,
valueString) tuples. The
REQUIRED name parameter of each component identifier contains the encoded
nameString of a single query parameter as a String value. The component value of a single named parameter is the encoded
valueString of that single query parameter. Several different named query parameters
MAY be included in the covered components. Single named parameters
MAY occur in any order in the covered components, regardless of the order they occur in the query string.
The value of the
name parameter and the component value of a single named parameter are calculated via the following process:
-
Parse the nameString or valueString of the named query parameter defined by Section [HTMLURL] of [HTMLURL]; this is the value after percent-encoded octets are decoded.
-
Encode the nameString or valueString using the "percent-encode after encoding" process defined by Section [HTMLURL] of [HTMLURL]; this results in an ASCII string [ASCII].
-
Output the ASCII string.
Note that the component value does not include any leading question mark (
?) characters, equals sign (
=) characters, or separating ampersand (
&) characters. Named query parameters with an empty
valueString have an empty string as the component value. Note that due to inconsistencies in implementations, some query parameter parsing libraries drop such empty values.
If a query parameter is named as a covered component but it does not occur in the query parameters, this
MUST cause an error in the signature base generation.
For example, for the following request:
GET /path?param=value&foo=bar&baz=batman&qux= HTTP/1.1
Host: www.example.com
Indicating the
baz,
qux, and
param named query parameters would result in the following
@query-param component values:
baz:
batman
qux: an empty string
param:
value
and the following signature base lines, with (SP) indicating a single trailing space character before the empty component value:
"@query-param";name="baz": batman
"@query-param";name="qux":(SP)
"@query-param";name="param": value
This derived component has some limitations. Specifically, the algorithms provided in Section [
HTMLURL] of [
HTMLURL] only support query parameters using percent-escaped UTF-8 encoding. Other encodings are not supported. Additionally, multiple instances of a named parameter are not reliably supported in the wild. If a parameter name occurs multiple times in a request, the named query parameter
MUST NOT be included. If multiple parameters are common within an application, it is
RECOMMENDED to sign the entire query string using the
@query component identifier defined in
Section 2.2.7.
The encoding process allows query parameters that include newlines or other problematic characters in their values, or with alternative encodings such as using the plus (+) character to represent spaces. For the query parameters in this message:
NOTE: '\' line wrapping per RFC 8792
GET /parameters?var=this%20is%20a%20big%0Amultiline%20value&\
bar=with+plus+whitespace&fa%C3%A7ade%22%3A%20=something HTTP/1.1
Host: www.example.com
Date: Tue, 20 Apr 2021 02:07:56 GMT
The resulting values are encoded as follows:
"@query-param";name="var": this%20is%20a%20big%0Amultiline%20value
"@query-param";name="bar": with%20plus%20whitespace
"@query-param";name="fa%C3%A7ade%22%3A%20": something
If the encoding were not applied, the resultant values would be:
"@query-param";name="var": this is a big
multiline value
"@query-param";name="bar": with plus whitespace
"@query-param";name="façade\": ": something
This base string contains characters that violate the constraints on component names and values and is therefore invalid.
The
@status derived component refers to the three-digit numeric HTTP status code of a response message as defined in [
HTTP],
Section 15. The component value is the serialized three-digit integer of the HTTP status code, with no descriptive text.
For example, the following response message:
HTTP/1.1 200 OK
Date: Fri, 26 Mar 2010 00:05:00 GMT
would result in the following
@status component value:
and the following signature base line:
The
@status component identifier
MUST NOT be used in a request message.
HTTP message signatures have metadata properties that provide information regarding the signature's generation and verification, consisting of the ordered set of covered components and the ordered set of parameters, where the parameters include a timestamp of signature creation, identifiers for verification key material, and other utilities. This metadata is represented by a special message component in the signature base for signature parameters; this special message component is treated slightly differently from other message components. Specifically, the signature parameters message component is
REQUIRED as the last line of the
Section 2.5, and the component identifier
MUST NOT be enumerated within the set of covered components for any signature, including itself.
The signature parameters component name is
@signature-params.
The signature parameters component value is the serialization of the signature parameters for this signature, including the covered components ordered set with all associated parameters. These parameters include any of the following:
-
created:
-
Creation time as a UNIX timestamp value of type Integer. Sub-second precision is not supported. The inclusion of this parameter is RECOMMENDED.
-
expires:
-
Expiration time as a UNIX timestamp value of type Integer. Sub-second precision is not supported.
-
nonce:
-
A random unique value generated for this signature as a String value.
-
alg:
-
The HTTP message signature algorithm from the "HTTP Signature Algorithms" registry, as a String value.
-
keyid:
-
The identifier for the key material as a String value.
-
tag:
-
An application-specific tag for the signature as a String value. This value is used by applications to help identify signatures relevant for specific applications or protocols.
Additional parameters can be defined in the
Section 6.3. Note that the parameters are not in any general order, but once an ordering is chosen for a given set of parameters, it cannot be changed without altering the signature parameters value.
The signature parameters component value is serialized as a parameterized Inner List using the rules provided in
Section 4 of [
STRUCTURED-FIELDS] as follows:
-
Let the output be an empty string.
-
Determine an order for the component identifiers of the covered components, not including the @signature-params component identifier itself. Once this order is chosen, it cannot be changed. This order MUST be the same order as that used in creating the signature base (Section 2.5).
-
Serialize the component identifiers of the covered components, including all parameters, as an ordered Inner List of String values according to Section 4.1.1.1 of [STRUCTURED-FIELDS]; then, append this to the output. Note that the component identifiers can include their own parameters, and these parameters are ordered sets. Once an order is chosen for a component's parameters, the order cannot be changed.
-
Determine an order for any signature parameters. Once this order is chosen, it cannot be changed.
-
Append the parameters to the Inner List in order according to Section 4.1.1.2 of [STRUCTURED-FIELDS], skipping parameters that are not available or not used for this message signature.
-
The output contains the signature parameters component value.
Note that the Inner List serialization from
Section 4.1.1.1 of [
STRUCTURED-FIELDS] is used for the covered component value instead of the List serialization from
Section 4.1.1 of [
STRUCTURED-FIELDS] in order to facilitate parallelism with this value's inclusion in the Signature-Input field, as discussed in
Section 4.1.
This example shows the serialized component value for the parameters of an example message signature:
NOTE: '\' line wrapping per RFC 8792
("@target-uri" "@authority" "date" "cache-control")\
;keyid="test-key-rsa-pss";alg="rsa-pss-sha512";\
created=1618884475;expires=1618884775
Note that an HTTP message could contain
Section 4.3, but only the signature parameters used for a single signature are included in a given signature parameters entry.
When a request message results in a signed response message, the signer can include portions of the request message in the signature base by adding the
req parameter to the component identifier.
-
req
-
A Boolean flag indicating that the component value is derived from the request that triggered this response message and not from the response message directly.
This parameter can be applied to both HTTP fields and derived components that target the request, with the same semantics. The component value for a message component using this parameter is calculated in the same manner as it is normally, but data is pulled from the request message instead of the target response message to which the signature is applied.
Note that the same component name
MAY be included with and without the
req parameter in a single signature base, indicating the same named component from both the request message and the response message.
The
req parameter
MAY be combined with other parameters as appropriate for the component identifier, such as the
key parameter for a Dictionary field.
For example, when serving a response for this request:
NOTE: '\' line wrapping per RFC 8792
POST /foo?param=Value&Pet=dog HTTP/1.1
Host: example.com
Date: Tue, 20 Apr 2021 02:07:55 GMT
Content-Digest: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+T\
aPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==:
Content-Type: application/json
Content-Length: 18
{"hello": "world"}
This would result in the following unsigned response message:
NOTE: '\' line wrapping per RFC 8792
HTTP/1.1 503 Service Unavailable
Date: Tue, 20 Apr 2021 02:07:56 GMT
Content-Type: application/json
Content-Length: 62
Content-Digest: sha-512=:0Y6iCBzGg5rZtoXS95Ijz03mslf6KAMCloESHObfwn\
HJDbkkWWQz6PhhU9kxsTbARtY2PTBOzq24uJFpHsMuAg==:
{"busy": true, "message": "Your call is very important to us"}
The server signs the response with its own key, including the
@status code and several header fields in the covered components. While this covers a reasonable amount of the response for this application, the server additionally includes several components derived from the original request message that triggered this response. In this example, the server includes the method, authority, path, and content digest from the request in the covered components of the response. The Content-Digest for both the request and the response is included under the response signature. For the application in this example, the query is deemed not to be relevant to the response and is therefore not covered. Other applications would make different decisions based on application needs, as discussed in
Section 1.4.
The signature base for this example is:
NOTE: '\' line wrapping per RFC 8792
"@status": 503
"content-digest": sha-512=:0Y6iCBzGg5rZtoXS95Ijz03mslf6KAMCloESHObf\
wnHJDbkkWWQz6PhhU9kxsTbARtY2PTBOzq24uJFpHsMuAg==:
"content-type": application/json
"@authority";req: example.com
"@method";req: POST
"@path";req: /foo
"content-digest";req: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A\
2svX+TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==:
"@signature-params": ("@status" "content-digest" "content-type" \
"@authority";req "@method";req "@path";req "content-digest";req)\
;created=1618884479;keyid="test-key-ecc-p256"
The signed response message is:
NOTE: '\' line wrapping per RFC 8792
HTTP/1.1 503 Service Unavailable
Date: Tue, 20 Apr 2021 02:07:56 GMT
Content-Type: application/json
Content-Length: 62
Content-Digest: sha-512=:0Y6iCBzGg5rZtoXS95Ijz03mslf6KAMCloESHObfwn\
HJDbkkWWQz6PhhU9kxsTbARtY2PTBOzq24uJFpHsMuAg==:
Signature-Input: reqres=("@status" "content-digest" "content-type" \
"@authority";req "@method";req "@path";req "content-digest";req)\
;created=1618884479;keyid="test-key-ecc-p256"
Signature: reqres=:dMT/A/76ehrdBTD/2Xx8QuKV6FoyzEP/I9hdzKN8LQJLNgzU\
4W767HK05rx1i8meNQQgQPgQp8wq2ive3tV5Ag==:
{"busy": true, "message": "Your call is very important to us"}
Note that the ECDSA signature algorithm in use here is non-deterministic, meaning that a different signature value will be created every time the algorithm is run. The signature value provided here can be validated against the given keys, but newly generated signature values are not expected to match the example. See
Section 7.3.5.
Since the component values from the request are not repeated in the response message, the requester
MUST keep the original message component values around long enough to validate the signature of the response that uses this component identifier parameter. In most cases, this means the requester needs to keep the original request message around, since the signer could choose to include any portions of the request in its response, according to the needs of the application. Since it is possible for an intermediary to alter a request message before it is processed by the server, applications need to take care not to sign such altered values, as the client would not be able to validate the resulting signature.
It is also possible for a server to create a signed response in response to a signed request. For this example of a signed request:
NOTE: '\' line wrapping per RFC 8792
POST /foo?param=Value&Pet=dog HTTP/1.1
Host: example.com
Date: Tue, 20 Apr 2021 02:07:55 GMT
Content-Digest: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+T\
aPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==:
Content-Type: application/json
Content-Length: 18
Signature-Input: sig1=("@method" "@authority" "@path" "@query" \
"content-digest" "content-type" "content-length")\
;created=1618884475;keyid="test-key-rsa-pss"
Signature: sig1=:e8UJ5wMiRaonlth5ERtE8GIiEH7Akcr493nQ07VPNo6y3qvjdK\
t0fo8VHO8xXDjmtYoatGYBGJVlMfIp06eVMEyNW2I4vN7XDAz7m5v1108vGzaDljr\
d0H8+SJ28g7bzn6h2xeL/8q+qUwahWA/JmC8aOC9iVnwbOKCc0WSrLgWQwTY6VLp4\
2Qt7jjhYT5W7/wCvfK9A1VmHH1lJXsV873Z6hpxesd50PSmO+xaNeYvDLvVdZlhtw\
5PCtUYzKjHqwmaQ6DEuM8udRjYsoNqp2xZKcuCO1nKc0V3RjpqMZLuuyVbHDAbCzr\
0pg2d2VM/OC33JAU7meEjjaNz+d7LWPg==:
{"hello": "world"}
The server could choose to sign portions of this response, including several portions of the request, resulting in this signature base:
NOTE: '\' line wrapping per RFC 8792
"@status": 503
"content-digest": sha-512=:0Y6iCBzGg5rZtoXS95Ijz03mslf6KAMCloESHObf\
wnHJDbkkWWQz6PhhU9kxsTbARtY2PTBOzq24uJFpHsMuAg==:
"content-type": application/json
"@authority";req: example.com
"@method";req: POST
"@path";req: /foo
"@query";req: ?param=Value&Pet=dog
"content-digest";req: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A\
2svX+TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==:
"content-type";req: application/json
"content-length";req: 18
"@signature-params": ("@status" "content-digest" "content-type" \
"@authority";req "@method";req "@path";req "@query";req \
"content-digest";req "content-type";req "content-length";req)\
;created=1618884479;keyid="test-key-ecc-p256"
and the following signed response:
NOTE: '\' line wrapping per RFC 8792
HTTP/1.1 503 Service Unavailable
Date: Tue, 20 Apr 2021 02:07:56 GMT
Content-Type: application/json
Content-Length: 62
Content-Digest: sha-512=:0Y6iCBzGg5rZtoXS95Ijz03mslf6KAMCloESHObfwn\
HJDbkkWWQz6PhhU9kxsTbARtY2PTBOzq24uJFpHsMuAg==:
Signature-Input: reqres=("@status" "content-digest" "content-type" \
"@authority";req "@method";req "@path";req "@query";req \
"content-digest";req "content-type";req "content-length";req)\
;created=1618884479;keyid="test-key-ecc-p256"
Signature: reqres=:C73J41GVKc+TYXbSobvZf0CmNcptRiWN+NY1Or0A36ISg6ym\
dRN6ZgR2QfrtopFNzqAyv+CeWrMsNbcV2Ojsgg==:
{"busy": true, "message": "Your call is very important to us"}
Note that the ECDSA signature algorithm in use here is non-deterministic, meaning that a different signature value will be created every time the algorithm is run. The signature value provided here can be validated against the given keys, but newly generated signature values are not expected to match the example. See
Section 7.3.5.
Applications signing a response to a signed request
SHOULD sign all of the components of the request signature value to provide sufficient coverage and protection against a class of collision attacks, as discussed in
Section 7.3.7. The server in this example has included all components listed in the Signature-Input field of the client's signature on the request in the response signature, in addition to components of the response.
While it is syntactically possible to include the Signature and Signature-Input fields of the request message in the signature components of a response to a message using this mechanism, this practice is
NOT RECOMMENDED. This is because signatures of signatures do not provide transitive coverage of covered components as one might expect, and the practice is susceptible to several attacks as discussed in
Section 7.3.7. An application that needs to signal successful processing or receipt of a signature would need to carefully specify alternative mechanisms for sending such a signal securely.
The response signature can only ever cover what is included in the request message when using this flag. Consequently, if an application needs to include the message content of the request under the signature of its response, the client needs to include a means for covering that content, such as a Content-Digest field. See the discussion in
Section 7.2.8 for more information.
The
req parameter
MUST NOT be used for any component in a signature that targets a request message.
The signature base is an ASCII string [
ASCII] containing the canonicalized HTTP message components covered by the signature. The input to the signature base creation algorithm is the ordered set of covered component identifiers and their associated values, along with any additional signature parameters discussed in
Section 2.3.
Component identifiers are serialized using the strict serialization rules defined by [
STRUCTURED-FIELDS],
Section 4. The component identifier has a component name, which is a String Item value serialized using the
sf-string ABNF rule. The component identifier
MAY also include defined parameters that are serialized using the
parameters ABNF rule. The signature parameters line defined in
Section 2.3 follows this same pattern, but the component identifier is a String Item with a fixed value and no parameters, and the component value is always an Inner List with optional parameters.
Note that this means the serialization of the component name itself is encased in double quotes, with parameters following as a semicolon-separated list, such as
"cache-control",
"@authority",
"@signature-params", or
"example-dictionary";key="foo".
The output is the ordered set of bytes that form the signature base, which conforms to the following ABNF:
signature-base = *( signature-base-line LF ) signature-params-line
signature-base-line = component-identifier ":" SP
( derived-component-value / *field-content )
; no obs-fold nor obs-text
component-identifier = component-name parameters
component-name = sf-string
derived-component-value = *( VCHAR / SP )
signature-params-line = DQUOTE "@signature-params" DQUOTE
":" SP inner-list
To create the signature base, the signer or verifier concatenates entries for each component identifier in the signature's covered components (including their parameters) using the following algorithm. All errors produced as described
MUST fail the algorithm immediately, without outputting a signature base.
-
Let the output be an empty string.
-
For each message component item in the covered components set (in order):
- 2.1.
- If the component identifier (including its parameters) has already been added to the signature base, produce an error.
- 2.2.
- Append the component identifier for the covered component serialized according to the component-identifier ABNF rule. Note that this serialization places the component name in double quotes and appends any parameters outside of the quotes.
- 2.3.
- Append a single colon (:).
- 2.4.
- Append a single space (" ").
- 2.5.
-
Determine the component value for the component identifier.
-
If the component identifier has a parameter that is not understood, produce an error.
-
If the component identifier has parameters that are mutually incompatible with one another, such as bs and sf, produce an error.
-
If the component identifier contains the req parameter and the target message is a request, produce an error.
-
If the component identifier contains the req parameter and the target message is a response, the context for the component value is the related request message of the target response message. Otherwise, the context for the component value is the target message.
-
If the component name starts with an "at" (@) character, derive the component's value from the message according to the specific rules defined for the derived component, as provided in Section 2.2, including processing of any known valid parameters. If the derived component name is unknown or the value cannot be derived, produce an error.
-
If the component name does not start with an "at" (@) character, canonicalize the HTTP field value as described in Section 2.1, including processing of any known valid parameters. If the field cannot be found in the message or the value cannot be obtained in the context, produce an error.
- 2.6.
- Append the covered component's canonicalized component value.
- 2.7.
- Append a single newline (\n).
-
Append the signature parameters component (Section 2.3) according to the signature-params-line rule as follows:
- 3.1.
- Append the component identifier for the signature parameters serialized according to the component-identifier rule, i.e., the exact value "@signature-params" (including double quotes).
- 3.2.
- Append a single colon (:).
- 3.3.
- Append a single space (" ").
- 3.4.
- Append the signature parameters' canonicalized component values as defined in Section 2.3, i.e., Inner List Structured Field values with parameters.
-
Produce an error if the output string contains any non-ASCII characters [ASCII].
-
Return the output string.
If covered components reference a component identifier that cannot be resolved to a component value in the message, the implementation
MUST produce an error and not create a signature base. Such situations include, but are not limited to, the following:
-
The signer or verifier does not understand the derived component name.
-
The component name identifies a field that is not present in the message or whose value is malformed.
-
The component identifier includes a parameter that is unknown or does not apply to the component identifier to which it is attached.
-
The component identifier indicates that a Structured Field serialization is used (via the sf parameter), but the field in question is known to not be a Structured Field or the type of Structured Field is not known to the implementation.
-
The component identifier is a Dictionary member identifier that references a field that is not present in the message, that is not a Dictionary Structured Field, or whose value is malformed.
-
The component identifier is a Dictionary member identifier or a named query parameter identifier that references a member that is not present in the component value or whose value is malformed. For example, the identifier is "example-dict";key="c", and the value of the Example-Dict header field is a=1, b=2, which does not have the c value.
In the following non-normative example, the HTTP message being signed is the following request:
NOTE: '\' line wrapping per RFC 8792
POST /foo?param=Value&Pet=dog HTTP/1.1
Host: example.com
Date: Tue, 20 Apr 2021 02:07:55 GMT
Content-Type: application/json
Content-Digest: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+T\
aPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==:
Content-Length: 18
{"hello": "world"}
The covered components consist of the
@method,
@authority, and
@path derived components followed by the
Content-Digest,
Content-Length, and
Content-Type HTTP header fields, in order. The signature parameters consist of a creation timestamp of
1618884473 and a key identifier of
test-key-rsa-pss. Note that no explicit
alg parameter is given here, since the verifier is known by the application to use the RSA-PSS algorithm based on the identified key. The signature base for this message with these parameters is:
NOTE: '\' line wrapping per RFC 8792
"@method": POST
"@authority": example.com
"@path": /foo
"content-digest": sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX\
+TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==:
"content-length": 18
"content-type": application/json
"@signature-params": ("@method" "@authority" "@path" \
"content-digest" "content-length" "content-type")\
;created=1618884473;keyid="test-key-rsa-pss"
Note that the example signature base above does not include the final newline that ends the displayed example, nor do other example signature bases displayed elsewhere in this specification.