You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@commons.apache.org by "Dan Mclaughlin (Jira)" <ji...@apache.org> on 2026/10/21 20:04:00 UTC

[jira] [Updated] (CODEC-348) Base64 STRICT decoding in 1.23.0 rejects valid input when the codec has a line length (breaking change from 1.22.1)

     [ https://issues.apache.org/jira/browse/CODEC-348?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Dan Mclaughlin updated CODEC-348:
---------------------------------
    Attachment: commons-codec-1.23-strict-repro.zip

> Base64 STRICT decoding in 1.23.0 rejects valid input when the codec has a line length (breaking change from 1.22.1)
> -------------------------------------------------------------------------------------------------------------------
>
>                 Key: CODEC-348
>                 URL: https://issues.apache.org/jira/browse/CODEC-348
>             Project: Commons Codec
>          Issue Type: Bug
>    Affects Versions: 1.23.0
>         Environment: OpenJDK 21.0.12
>            Reporter: Dan Mclaughlin
>            Priority: Major
>         Attachments: commons-codec-1.23-strict-repro.zip
>
>
> Component: {{org.apache.commons.codec.binary.Base64}} / {{BaseNCodec}}
> Works in: 1.22.1 and earlier (STRICT since 1.15)
> h2. Summary
> Commit {{8b506a1c}} ("Base32 and Base64 STRICT decoding now require the encoder's canonical alphabet, padding, and line separators, and validate streams through EOF") changed what {{CodecPolicy.STRICT}} means for decoding. A STRICT {{Base64}} instance constructed with a line length now accepts only byte-for-byte what that instance would encode, including a trailing line separator. Previously valid input that decoded correctly in 1.22.1 now throws {{IllegalArgumentException}}.
> This is a behavior change in a minor release that breaks existing callers who use one STRICT instance for both encoding and decoding. Shibboleth's {{Base64Support}} (used by the Shibboleth IdP, SP integrations and OpenSAML for all SAML/XML-DSig base64) is one such caller: with 1.23.0 it can no longer decode its own encoded output. This is tracked downstream as Shibboleth [JSSH-84|https://shibboleth.atlassian.net/browse/JSSH-84] (priority Highest), where it breaks SAML message, signature and certificate decoding; in our deployment it broke SAML metadata signature validation. Because the IdP does not control which commons-codec version deployers' builds resolve, every Shibboleth deployment whose dependency management moves to 1.23.0 is affected.
> h2. Steps to reproduce
> Attached {{CodecOnlyRepro.java}} (commons-codec only):
> {code:java}
> Base64 strict76 = new Base64(76, new byte[] {'\n'}, false, CodecPolicy.STRICT);
> String exact = strict76.encodeToString(data);
> strict76.decode(exact);                          // exact encoder output
> strict76.decode(exact.trim());                   // same, trailing '\n' removed
> strict76.decode(singleLineEncodingOf(data));     // same bytes, no line breaks
> strict76.decode(exact.replace("\n", "\r\n"));    // same bytes, CRLF separators
> {code}
> h2. Expected
> All four decode to the original bytes, as in 1.22.1. Each input is valid base64 for the same data, with canonical alphabet, padding and trailing bits.
> h2. Actual (1.23.0)
> ||Input||20 bytes||256 bytes||
> |Exact encoder output|OK|OK|
> |Encoder output, trailing {{\n}} trimmed|Missing or incomplete final line separator|Missing or incomplete final line separator|
> |Single line, no separators|Missing or incomplete final line separator|Invalid line separator or line length|
> |76-char lines, CRLF|Unexpected character or data after padding|Invalid line separator or line length|
> In 1.22.1 every case returns the correct bytes.
> h2. Why this is a compatibility problem
> * Through 1.22.1, {{BaseNCodec.isStrictDecoding()}} documented STRICT narrowly: decoding raises {{IllegalArgumentException}} "if trailing bits are not part of a valid encoding." Callers chose STRICT for that guarantee, not for line-structure validation.
> * {{CodecPolicy.STRICT}}'s Javadoc is unchanged in 1.23.0 ("Data that causes a codec to fail should throw an exception"), so nothing at the API level signals the broader meaning.
> * The changelog entry lists it under fixed bugs, and the release notes describe 1.23.0 as a "feature and maintenance release," so downstream consumers and automated update tools treat it as a safe minor bump.
> * Requiring a trailing separator even for input shorter than one line is stricter than RFC 2045 and RFC 4648 practice, and rejects the common pattern of trimming encoded output.
> * In CODEC-346 (resolved 2026-10-03), a request to make default decoding stricter was declined because "changing the default would introduce another compatibility break." Changing what STRICT itself accepts is the same kind of break for callers who already opted into STRICT.
> h2. Requests (any one would resolve it)
> # Make line-structure validation opt-in (for example a builder option such as {{setStrictLineValidation(true)}}), keeping 1.22.x STRICT semantics as the default.
> # Apply line-structure validation only when explicitly requested, and continue to accept input without separators (or with a missing final separator) for instances that have a line length.
> # If the new behavior stays, document it as a breaking change in the release notes and in the {{CodecPolicy.STRICT}} and {{isStrictDecoding()}} Javadoc, and state that an instance with a line length should not be used to decode arbitrary input.
> h2. Workaround for affected users
> Decode with an instance that has no line length, after removing whitespace:
> {code:java}
> new Base64(0, new byte[] {'\n'}, false, CodecPolicy.STRICT).decode(input.replaceAll("\\s+", ""));
> {code}
> This decodes all the inputs above in both 1.22.1 and 1.23.0, and still rejects non-canonical trailing bits.
> h2. Attachments
> {{commons-codec-1.23-strict-repro}} project: {{CodecOnlyRepro.java}}, {{run.sh}}, {{run-output.txt}} (output for 1.22.1 and 1.23.0). Also includes the Shibboleth {{Base64Support}} and OpenSAML reproductions showing downstream impact.
> Related: Shibboleth [JSSH-84|https://shibboleth.atlassian.net/browse/JSSH-84] (downstream impact), CODEC-346 (compatibility rationale), commit {{8b506a1c}} (the change).



--
This message was sent by Atlassian Jira
(v8.20.10#820010)