SReverseby Simpa Labs

Request signing

Sorting and encoding rules in a signed query string

Your query string matches the capture character for character and the signature is still refused. The server canonicalises the query before signing it, and your canonical form differs from its own.

The signature usually covers a canonical query, not the URL

A signing scheme rarely hashes the URL exactly as the client wrote it. It defines a canonical form first, then computes over that form. Two clients can send different URLs that reduce to the same canonical string and both valid, or send identical URLs that reduce differently and one fails. When a signed query is refused, the difference is almost always in the canonicalisation rules rather than in the secret.

The rules vary by provider, so the useful output of an investigation is not a rule that holds everywhere but the specific rule set this endpoint applies. Each rule can be tested against a captured request and response pair, one change at a time.

Sorted order and insertion order

Some schemes sort parameters by name before signing, and some sign the order the client sent. The two agree only when the client happens to send them in sorted order.

Sorting itself has variants. A byte-wise sort on the encoded names is the most common. A sort after percent-decoding can order names differently when one contains an encoded character. A case-insensitive sort puts an uppercase name after a lowercase one, and a case-sensitive sort does the reverse.

The test is direct. Reorder a parameter in the captured query and send the request unchanged otherwise. If the response is unchanged, the server sorts and your order is irrelevant. If the response changes to a signature error, the server signs the order it received and your client has to preserve it. A server that sorts still verifies a request that arrives sorted, so repeat the test with parameters sent deliberately out of order.

Repeated parameters

Repeated names have no single convention. Some schemes include every occurrence as its own pair, some keep the first, some keep the last, and some join the values into a list with a separator.

  • Keeping every pair. The canonical string contains the name once per occurrence, in a defined order among themselves.
  • Keeping one value. The verifier builds a map and one of the duplicates overwrites the other, which is common when the query is parsed into a dictionary before signing.
  • Joining the values. The duplicates become a single entry whose value is the values concatenated with a comma or another separator.
  • Sorting the values too. Where names are sorted, the values for a repeated name may be sorted among themselves or left in order.

The test uses a duplicated parameter with the same value twice, so the request still makes sense to the server. If the request passes, the verifier either keeps every pair or joins equal values. If it fails, the verifier keeps one occurrence. Changing the two values so they differ, then swapping their order, separates the remaining variants.

Empty values and missing parameters

An empty value and an absent parameter are different things on the wire, and verifiers disagree about whether they are different in the canonical form.

  • Empty and absent are distinct. The canonical string contains the name with an empty value when the client sent it that way, and omits the name entirely when it did not.
  • Empty means absent. The parser drops a parameter whose value is empty, so sending it changes nothing.
  • A trailing marker stays. Where the canonical string is built by joining name and value with a separator, an empty value leaves the separator in place, and a verifier that trims it computes a different digest.

Add a parameter with an empty value to a working capture. A pass means the verifier tolerates the addition, and a signature error means the canonical form includes it. Removing a parameter that the endpoint does not need separates the two directions.

Encoding once, or twice

Percent-encoding is where most mismatches happen, because the rules differ across languages and across schemes.

  • Spaces. A space is encoded as a percent sign followed by 20 in one convention and as a plus sign in another, and the two are not interchangeable in a signature.
  • Hexadecimal case. One library emits uppercase hex digits and another emits lowercase, which changes the canonical string without changing the decoded value.
  • The unreserved set. Implementations disagree about which characters may stay unencoded, so a value containing a colon, a tilde, or a bracket sits in a grey area.
  • Encoding twice. When the value itself contains a percent sign or an already encoded sequence, one implementation encodes the original bytes and another encodes the encoded text, which is the double encoding case.

To test the pair, take a parameter that holds a value with a character in the disputed set, re-encode it in the alternative convention, and compare the response. Change the case of one encoded hex pair next. Where the value contains a percent sign, test the double encoding case by encoding it once and twice and sending each.

Testing each rule against a captured pair

Keep a captured request that is known to work, and treat changes to it as the experiment. Change one element, send, and record the status and the body. Preserve the response for every failure, because a signature error and a general validation error look different and only one of them tells you the query was the problem.

Two cautions belong here. A parameter whose value is itself a query string has to be encoded inside the enclosing string, and its inner separators may fall under the same rules. Parameters in the request body are often canonicalised separately from those in the query, so a rule that holds for the query may not hold for the form.

Once the rules are written down, the client applies them in the same order the server does. Keep that normalisation step separate from the signing function, so a mismatch can be localised to the normaliser rather than the algorithm.

Reviewed 28 September 2026 · SReverse research desk

Start your full APK reconstruction

Send the full APK

Projects start at $120. Choose WhatsApp or email, then attach the APK in the app that opens. We reply within one hour with the next step and send the fixed quote after review.

Want us to contact you?