Skip to content

Review which characters should be trimmed for cookie and header data #1084

Open

Description

@jrfnl

Setting the stage

By default, the PHP native [lr]trim() functions, trim ASCII whitespace and the NUL byte character.
PHP 8.6 changes the default value of the $characters parameter to also include the form feed - "\f" - character, which was previously not trimmed.

⚠️ Keep in mind that [lr]trim() only operates on the leading and/or trailing characters for a text string. It does not affect the characters in the "middle" !

Refs:

What has already been done

Requests uses [lr]trim() in various places throughout the codebase.
To make this code PHP cross-version compatible, PR #1083 made the following changes:

  • Introduced two class constants to represent the different default values for the $characters parameter.
  • Made the $characters being trimmed explicit in each of the [lr]trim() function calls (if it wasn't already).

To determine which characters should be trimmed, the following rule of thumb has been used for the initial changes made in PR #1083:
"Use the PHP 8.6 default (Trim::WHITESPACE_CHARS), except when the trimming may be subject to an RFC or other documented rules, in which case use the PHP < 8.6 default (Trim::WHITESPACE_CHARS_NO_FF)"

That way, we preserved existing behaviour in "important" places, while benefitting from the new default value everywhere else.

Task

For all calls to [lr]trim() which currently use the Trim::WHITESPACE_CHARS_NO_FF value for $characters, it should be researched whether that is correct by checking RFCs and other applicable rules. In some cases, it may also be prudent to evaluate how other projects similar to Requests, like for instance Guzzle, handle these text strings.

If the trimming is correct, it should be documented that this was verified via a comment in this issue.

If a change is necessary, please submit a PR and include tests safeguarding the change to the value passed for $characters.

After PR #1083, there will be 14 places in the codebase where this review task is necessary.

Activity

  1. asllanmaciel commented on Sep 16, 2026

    @asllanmaciel

    I reviewed all 14 Trim::WHITESPACE_CHARS_NO_FF call sites against the protocol rules, using the current #1083 head (6d9e4192f6714c652ae80cb71f590d6073234b9). For the PHP 8.6 compatibility question in this issue, I do not think any of these sites should start trimming form feed (\f).

    HTTP response/header parsing

    src/Requests.php (header value) and src/Transport/Curl.php (raw response headers): RFC 9110 §5.6.3 defines OWS/BWS as only SP / HTAB; form feed is not HTTP whitespace. RFC 9110 §5.5 also excludes leading/trailing whitespace from the field value after parsing. So preserving the pre-8.6 character set is preferable to silently accepting/trimming \f.

    Reference: https://www.rfc-editor.org/rfc/rfc9110.html#section-5.6.3

    Chunked transfer decoding

    The three calls in Requests::decode_chunked() should also keep \f significant. RFC 9112 §7.1 defines chunk-size = 1*HEXDIG, chunk framing with exact CRLF, and chunk-extension BWS via HTTP BWS (SP/HTAB). Form feed is not permitted framing whitespace and should not be normalized away.

    Reference: https://www.rfc-editor.org/rfc/rfc9112.html#section-7.1

    One of the trims (trim($matches[1], ...)) is effectively redundant because the regexp captures only hex digits, but changing that is outside the PHP 8.6 compatibility scope.

    Compressed response bodies

    The checks in Requests::decompress() and Requests::compatible_gzinflate() operate on binary entity data. There is no protocol rule under which a form-feed octet makes a binary body "empty". Keeping \f out of the trim set avoids reclassifying a non-empty binary payload as empty/false.

    TLS subjectAltName parsing

    The two Ssl::verify_certificate() trims process OpenSSL's textual rendering of subjectAltName. RFC 5280 defines dNSName as IA5String using the preferred DNS name syntax; form feed is not valid DNS-name whitespace. It therefore should not be silently stripped from the name.

    Reference: https://www.rfc-editor.org/rfc/rfc5280.html#section-4.2.1.6

    Cookie parsing

    The five calls in Cookie::parse() should not add form feed either. RFC 6265 §5.2 explicitly says to remove leading/trailing WSP from the cookie name/value, where WSP is SP/HTAB. Attribute parsing similarly does not define form feed as ignorable whitespace. This is a particularly strong reason to keep WHITESPACE_CHARS_NO_FF for the PHP 8.6 compatibility change.

    Reference: https://www.rfc-editor.org/rfc/rfc6265.html#section-5.2

    Scope note

    This review only answers the issue's PHP 8.6 question: should \f newly become trimmable at these 14 call sites? My conclusion is no for all 14.

    There is a separate possible hardening/standards-cleanup question: some of the current calls trim more than the protocol-defined SP/HTAB (for example CR/LF/VT/NUL via PHP's historical default). Tightening those sets could change long-standing permissive behavior and should be evaluated separately with dedicated compatibility tests rather than folded into #1083.

  2. jrfnl commented on Sep 16, 2026

    @jrfnl
    MemberAuthor

    I reviewed all 14 Trim::WHITESPACE_CHARS_NO_FF call sites against the protocol rules, using the current #1083 head (6d9e4192f6714c652ae80cb71f590d6073234b9).

    <snip>

    Scope note

    This review only answers the issue's PHP 8.6 question: should \f newly become trimmable at these 14 call sites? My conclusion is no for all 14.

    There is a separate possible hardening/standards-cleanup question: some of the current calls trim more than the protocol-defined SP/HTAB (for example CR/LF/VT/NUL via PHP's historical default). Tightening those sets could change long-standing permissive behavior and should be evaluated separately with dedicated compatibility tests rather than folded into #1083.

    @asllanmaciel Thank you for starting this review and for including links to all the sources you base your conclusions on. This helps a lot!

    The scope of this issue is not limited to the PHP 8.6 change, so the "separate possible hardening/standards-cleanup question" is the task and based on your analysis, it reads to me that some changes may be warranted as a follow up to PR #1083 (which should have been merged after the approval, not sure why that didn't happen - will merge now to make it more straight-forward to start work on the follow-up).

    Would you be willing to create such a follow-up PR (including adding tests to safeguard the hardening) ?

    I imagine adding additional WHITESPACE_CHARS_RFC### constants in the new Trim class would be a good way to document the specifications and ensure the right set of characters is used in various places.
    I also imagine that where form feed (or specific other) whitespace isn't allowed at all, it may be a reason not to accept the input by throwing an exception instead of trimming ?

    How does that sound to you ?


    For the record for anyone who comes across this issue and is wondering what the abbreviations mean:

    The following core rules are included by reference, as defined in Appendix B.1 of [RFC5234]: ALPHA (letters), CR (carriage return), CRLF (CR LF), CTL (controls), DIGIT (decimal 0-9), DQUOTE (double quote), HEXDIG (hexadecimal 0-9/A-F/a-f), HTAB (horizontal tab), LF (line feed), OCTET (any 8-bit sequence of data), SP (space), and VCHAR (any visible US-ASCII character).

  3. masakielastic commented on Sep 17, 2026

    @masakielastic

    One update to the Cookie analysis: RFC 6265 has now been obsoleted by RFC 10025 (Cookies: HTTP State Management Mechanism, July 2026), so the Cookie-related review should probably use RFC 10025 as the primary reference.

    The conclusion about whitespace remains the same, but RFC 10025 also clarifies the handling of control characters.

    In the Set-Cookie parsing algorithm (Section 5.6), a user agent rejects the entire set-cookie-string if it contains:

    %x00-08 / %x0A-1F / %x7F
    

    HTAB (%x09) is excluded because it is handled as whitespace.

    After that validation, leading and trailing WSP are removed from cookie names/values and attribute names/values:

    WSP = SP / HTAB

    This means form feed (%x0C) should not merely be left untrimmed: it is a disallowed control character and should cause the Set-Cookie input to be rejected.

    So RFC 10025 appears to support separating two responsibilities:

    validation:
        reject CTLs other than HTAB
    
    normalization:
        trim SP / HTAB where WSP is allowed
    

    A follow-up PR may therefore need to consider explicit CTL validation as well as narrowing the trimming character set.

    RFC 10025 also defines the server-side Set-Cookie grammar using HTTP BWS and OWS, which further supports keeping protocol-specific whitespace rules separate from PHP's native trim() defaults.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions