Skip to content

JWE segments are not base64url encoded: / is never substituted #103

Description

@mmorales-post

toEncodedString in lib/mcapi/crypto/jwe-crypto.js converts standard base64 to base64url
by hand. The second substitution matches a backslash rather than a forward slash:

result = result.replace(/\+/g, "-");
result = result.replace(/\\/g, "_");   // matches "\", should match "/"
return result.replace(/=/g, "");

Base64 output never contains a backslash, so that line is a no-op and every / survives
into the emitted token.

Impact

toEncodedString produces all five JWE segments, so this affects the header, encrypted key,
IV, ciphertext and auth tag. Measured over 200 encryptions with the repo's own
test/mock/jwe-config:

Segment Tokens containing a raw /
header 0 / 200
encrypted key 198 / 200
IV 66 / 200
ciphertext 178 / 200
auth tag 64 / 200

Every one of the 200 tokens contained at least one raw /. The encrypted key is 342
base64 characters, so a / is close to certain in that segment alone.

Why this has not caused failures

/ and _ both encode the value 63, and most decoders accept either alphabet. Node's
Buffer.from(value, "base64") is lenient, so the library's own decryptData round-trips
its own output, and services decoding with a permissive decoder are unaffected. This is a
conformance and interoperability defect rather than an outage.

It does matter for:

  • RFC 7515 section 2, which defines JOSE segments as base64url. Strict JOSE parsers reject
    a payload containing /.
  • The reason base64url exists. A raw / is a path delimiter, so a token cannot be safely
    placed in a URL path segment or a filename.
  • Any consumer that is not Mastercard's own gateway.

Suggested fix

result.replace(/\//g, "_"). One character. Both the current and corrected forms decrypt
successfully, so the change is backward compatible with anything already decoding these
tokens.

Buffer.prototype.toString("base64url") would be the more idiomatic fix, but it requires
Node 15 and package.json declares engines.node >= 6.12.3, so the manual substitution
is kept.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions