Watch at EaseStories. Answers. Perspective.

Technology · PRACTICAL GUIDE

Python Base64 validate=True still accepts aliases? Check pad bits and runtime

Strict decoding and a single canonical spelling are different checks. Seventeen local controls separate pad bits, runtime options, size limits and the meaning of decoded bytes.

Original technical illustration comparing Zg== with Zh== for f and Zm8= with Zm9= for fo, including the final six-bit groups on the tested CPython 3.12.14 runtime.
Original technical illustration: strict decoding accepted both spellings in each pair; re-encoding produced only Zg== and Zm8=. These are local CPython 3.12.14 results. Watch at Ease

On our CPython 3.12.14 runtime, base64.b64decode(value,validate=True) accepted both Zg== and Zh== as the byte f. It also accepted Zm8= and Zm9= as fo. Successful strict decoding did not prove that the encoded text was the canonical spelling.

Start with the protocol you actually need. This guide tests one narrow policy: nonempty bytes containing standard-alphabet Base64 with required padding, bounded lengths and an exact re-encoding match. It does not validate JWTs, signatures or MIME messages.

1. Separate padding characters from unused pad bits

The tested Zg== and Zh== inputs have the same number of equals signs. Their difference is in unused bits of the final alphabet character. Strict decoding accepted both; re-encoding the returned byte produced Zg==, so the second spelling failed our equality check. The same contrast held for Zm8= and Zm9=.

RFC 4648 requires conforming encoders to set unused pad bits to zero. It permits decoders to reject nonzero pad bits, with the referring protocol deciding stricter requirements. That is why decoder acceptance alone does not settle whether an input meets an application’s one-spelling rule.

2. Check the runtime before using a new keyword

Python 3.15 documentation adds canonical=True to reject nonzero pad bits. Keep alphabet validation explicit with validate=True as well, and retain the protocol’s padding and size rules. We read that documentation but did not run Python 3.15.

Passing canonical=True on the actual CPython 3.12.14 interpreter raised TypeError for an unexpected keyword. In that environment, decode with validate=True and require base64.b64encode(decoded)==encoded for this standard padded policy. The comparison checks representation, not a MAC or signature.

3. Apply the complete bounded policy

Our helper first requires an exact bytes input and rejects encoded values longer than 4096 bytes. It strictly decodes, rejects decoded output above 3072 bytes, applies the application’s nonempty rule and compares the standard re-encoding with the original input. It returns bytes only after all checks pass.

The 17 controls included valid f, fo and foo encodings; the two pad-bit aliases; redundant or excess padding; a newline; missing required padding; a URL-safe character; trailing data after padding; and empty input. Additional controls verified the pre-decode bound, decoded-size bound, unsupported keyword and separate UTF-8 failure.

4. Keep format acceptance separate from payload meaning

The complete three-byte value foo canonically encodes as Zm9v without equals signs. Required padding does not mean adding = to every input: our strict decoder rejected Zm9v=. Empty Base64 decoded successfully, but our application deliberately rejected an empty payload. Those are separate facts and policies.

The canonical input /w== decoded to byte 0xff and passed our representation policy; decoding that byte as UTF-8 failed. Format validity therefore does not prove valid text, a valid file or authenticity. Keep the original failed input for diagnosis, and do not silently strip characters or append padding when the declared wire contract requires an exact form.

Original technical illustration showing encoded bytes limit, strict decoding, decoded size and nonempty policy, and exact re-encoding comparison.
Original technical illustration: bytes-only input, 4096-byte encoded and 3072-byte decoded limits and no empty payload are this fixture’s policy. Python 3.15 canonical support was checked in documentation only. Watch at Ease