fix: sign the signed-fetch metadata verbatim instead of folding it - #322
Open
LautaroPetaccio wants to merge 1 commit into
Open
fix: sign the signed-fetch metadata verbatim instead of folding it#322LautaroPetaccio wants to merge 1 commit into
LautaroPetaccio wants to merge 1 commit into
Conversation
ADR-44 built the payload as
`[METHOD, PATH, TIMESTAMP, METADATA].join(":").toLowerCase()` — folded
after the metadata was joined in, which left the metadata's casing
outside the signature. `{"signer":"decentraland-kernel-scene"}` and
`{"Signer":"decentraland-kernel-scene"}` produce byte-identical payloads
and share one valid signature, while the X-Identity-Metadata header is
delivered as written. A service comparing `metadata.signer` reads the
second as absent, so a request could be re-spelled in flight, keep a
valid signature, and bypass the check the property existed to enforce.
The payload now lowercases the method and the path only and joins the
metadata verbatim, binding every property name and value — including
service-defined ones — to the signature. Requests are otherwise
unchanged: the header carries the same JSON, only the signed string
differs. The superseded form is kept in the document, labelled, since
implementations need to recognise it during migration.
Notes for implementers cover the parts that are easy to get wrong:
serialize the metadata once and sign the exact bytes sent, rebuild the
payload from the metadata as delivered without normalizing it, compare
authorized-on properties exactly, and scope any legacy fallback to the
properties being authorized on while refusing unexpected spellings.
ADR-289 and ADR-180 are amended for consistency rather than rewritten.
ADR-289 asserts that its metadata is integrity-protected and its fields
are camelCase (`sceneId`, `hashPayload`, `realm.serverName`), which is
exactly what folding left unbound; its Security Considerations covered
the client-side trust boundary but not the request in flight, so that
gap is now stated. ADR-180 requires verifying `intent`, `signer` and
`isGuest`, so it now says those comparisons are exact.
ADR-49 is unchanged: it already concatenates the metadata verbatim
(`'x-identity-metadata:' + Metadata`), and its lowercasing applies to
header names and Content-Type, which is correct HTTP canonicalization.
All three amended documents are status Living. `make build` passes.
Deploying adr with
|
| Latest commit: |
ef27d98
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://dae8d556.adr-cvq.pages.dev |
| Branch Preview URL: | https://fix-adr-44-sign-metadata-ver.adr-cvq.pages.dev |
Contributor
|
✅ Approved by Claude, approved by Codex — amend signed-fetch ADRs to sign metadata verbatim instead of lowercasing, fixing case-sensitive property-name bypass Checked: all three changed files read in full context (ADR-44, ADR-289, ADR-180); ADR-49 verified as already correct; old-vs-new payload construction semantics; cross-document consistency of "verbatim" and "exact comparison" clauses; security implications; git title/branch conventions; CI status (build pass, Cloudflare Pages pass) Cross-model review by Jarvis 🤖 · head ef27d98 · Claude + Codex · Requested by Ignacio Mazzara (<@U9ETM8CJH>) via Slack |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
ADR-44 specifies the signed-fetch payload as:
The fold happens after the metadata is joined in, which leaves the metadata's casing outside the signature.
{"signer":"decentraland-kernel-scene"}and{"Signer":"decentraland-kernel-scene"}produce a byte-identical payload and therefore share one valid signature — whileX-Identity-Metadatais delivered exactly as written. A service comparingmetadata.signerreads the second as absent, so a request could be re-spelled in flight, keep a genuinely valid signature, and bypass the check that property was there to enforce.This amends the specification so the metadata is joined verbatim:
Method and path are still lowercased, so a signature does not depend on how a client spelled them. Requests are otherwise unchanged — the header carries the same JSON, only the signed string differs.
All three amended documents are status Living, which is the status meant to be amended in place rather than superseded.
Changes
ADR-44-signed-fetch.md— the substantive change.// Superseded. Do not use.label — implementations still need to recognise it while callers migrate.ADR-289-explorer-client-scenes-signed-fetch.md— consistency, not a rewrite.It states its metadata is part of the signed payload "ensuring its integrity and authenticity", and its fields are camelCase (
sceneId,hashPayload,realm.serverName) — precisely what folding left unbound. Its Security Considerations covered the client-side trust boundary (scenes cannot reach the metadata the client builds) but not the request once it leaves the client, so a short Metadata Integrity in Transit subsection now states that, and the Overview notes the metadata is joined verbatim per ADR-44.ADR-180-communication-adapters.md— one sentence.It requires verifying
intent,signerandisGuest, so it now says those comparisons are exact: a value or property name differing only in case must be refused rather than case-folded.Checked and deliberately left unchanged
'x-identity-metadata:' + Metadata), and its lowercasing applies to header names andContent-Type, which is correct HTTP canonicalization rather than metadata folding. It is also status Draft.lowercasesearch but are unrelated (Ethereum address formats; an HTTP method check in a service worker).How to Test
Passes, and ADR-44 / ADR-289 / ADR-180 render with the new sections.