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.
toEncodedStringinlib/mcapi/crypto/jwe-crypto.jsconverts standard base64 to base64urlby hand. The second substitution matches a backslash rather than a forward slash:
Base64 output never contains a backslash, so that line is a no-op and every
/survivesinto the emitted token.
Impact
toEncodedStringproduces 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:/Every one of the 200 tokens contained at least one raw
/. The encrypted key is 342base64 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'sBuffer.from(value, "base64")is lenient, so the library's owndecryptDataround-tripsits 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:
a payload containing
/./is a path delimiter, so a token cannot be safelyplaced in a URL path segment or a filename.
Suggested fix
result.replace(/\//g, "_"). One character. Both the current and corrected forms decryptsuccessfully, so the change is backward compatible with anything already decoding these
tokens.
Buffer.prototype.toString("base64url")would be the more idiomatic fix, but it requiresNode 15 and
package.jsondeclaresengines.node >= 6.12.3, so the manual substitutionis kept.