Hardening PHP against filter chain attacks

php.internals

Sjoerd Langkemper

115 days ago
I am working on some things to harden PHP against filter chain attacks: • PHP RFC: Limit maximum number of filter chains <https://wiki.php.net/rfc/limit-maximum-number-of-filter-chains> • Dechunk incorrectly truncates string when it starts with a hex character <https://github.com/php/php-src/issues/21983> Filter chains use php://filter/ URLs with many filters, which are useful in several attacks, described in the RFC. I propose to limit the number of filters, and make the dechunk filter less useful for attacks. Please let me know what you think about this. Regards, Sjoerd Langkemper

Derick Rethans

111 days ago
On Sat, 9 May 2026, Sjoerd Langkemper wrote:
> I am working on some things to harden PHP against filter chain attacks: > • PHP RFC: Limit maximum number of filter chains <https://wiki.php.net/rfc/limit-maximum-number-of-filter-chains> > • Dechunk incorrectly truncates string when it starts with a hex character <https://github.com/php/php-src/issues/21983>
> Filter chains use php://filter/ URLs with many filters, which are > useful in several attacks, described in the RFC. I propose to limit > the number of filters, and make the dechunk filter less useful for > attacks. Please let me know what you think about this.
I think this makes sense. I'll reiterate my reply to a private email about the questions under the "Open Issues" section: Your RFC says that most actual use is 1 or 2, with exploits requiring 100+. Setting the default limit to say 16 should allow for wacky use, but not the exploity variant. I would also likely find it better if there was no INI setting. If you *really* must use more than 16, then you can always recompile PHP. To answer your questions from your RFC:
> Exact Limit: Should the default be 5, 10, or 20? (Most exploits > require 50-100+).
As I said, I'm more confortable with a higher number and a really low one.
> What should the INI name be? filter.max_chain_depth
I prefer no new INI setting, but perhaps 'filter.max_chain_length'. It's not so much a depth, but a length.
> How should this be introduced and tightened? E.g. start with high hard > limit, or low limit and give a deprecation warning instead of an > error?
I wouldn't want to make this to complicated, and just have a hard limit that gives an error.
> Should exceeding the limit throw a ValueError (consistent with modern > PHP 8 APIs) or a Warning (consistent with legacy stream handling)?
I think this should be thrown a ValueError. cheers, Derick
-- https://derickrethans.nl | https://xdebug.org | https://dram.io Author of Xdebug. Like it? Consider supporting me: https://xdebug.org/support mastodon: @derickr@phpc.social @xdebug@phpc.social

jvoisin

111 days ago
> Your RFC says that most actual use is 1 or 2, with exploits > requiring 100+. Setting the default limit to say 16 should allow for > wacky use, but not the exploity variant. > > I would also likely find it better if there was no INI setting. If > you *really* must use more than 16, then you can always recompile > PHP.
Some types of exploits using filters chain need way less than 100: - https://www.synacktiv.com/publications/php-filter-chains-file-read-from-error-based-oracle - https://github.com/DownUnderCTF/Challenges_2022_Public/blob/main/web/minimal-php/solve/solution.py - https://github.com/ambionics/cnext-exploits/ Even https://github.com/synacktiv/php_filter_chain_generator, so generate the string `<?php phpinfo(); ?>` is about 100, and could likely be golfed to be below this threshold. And even those using 100+ chained filters can likely use multiple smaller chains instead. Limiting to 100 will block the low-hanging fruits, but it unlikely to solve the issue once and for all. Making it a configurable ini setting (with a sane default of 5?) would provide admins a way to tailor it to their application. I fail to come up with a legitimate usecase that would use more than 2 filters in the first place to be honest.
>> What should the INI name be? filter.max_chain_depth > > I prefer no new INI setting, but perhaps 'filter.max_chain_length'. > It's not so much a depth, but a length.
+1 for filter.max_chain_length

Sjoerd Langkemper

105 days ago
I made some updates to the PHP RFC: Limit maximum number of filter chains <https://wiki.php.net/rfc/limit-maximum-number-of-filter-chains>. Any more opinions on this? I am currently thinking about these specifics: • Set a limit of at most 16 filters in a php://filter URL. This is quite a high limit and won't prevent all attacks, but also has a negligable chance of breaking legimitate functionality. • Start with raising a deprecation warning, and in a later version give an actual error. This is technically a BC break, and it can't hurt to follow the proper path for this. • Hardcode the limit, don't provide a INI setting. I think it is unlikely that people want to change this limit. I think it is acceptable to require recompilation to change the limit. • Raise a warning and return false, instead of throwing an exception. This is how stream functions currently work. It is not pretty, but it is consistent. Regards, Sjoerd Langkemper

Jakub Zelenka

102 days ago
Hi, On Tue, May 19, 2026 at 1:02 PM Sjoerd Langkemper <sjoerd-php@linuxonly.nl> wrote:
> I made some updates to the PHP RFC: Limit maximum number of filter chains > <https://wiki.php.net/rfc/limit-maximum-number-of-filter-chains>. > > Any more opinions on this? I am currently thinking about these specifics: > > - Set a limit of at most 16 filters in a php://filter URL. This is > quite a high limit and won't prevent all attacks, but also has a negligable > chance of breaking legimitate functionality. > >
This sounds reasonable as a default.
> > - Start with raising a deprecation warning, and in a later version > give an actual error. This is technically a BC break, and it can't hurt to > follow the proper path for this. > >
This might be a bit safer in terms of BC.
> > - Hardcode the limit, don't provide a INI setting. I think it is > unlikely that people want to change this limit. I think it is acceptable to > require recompilation to change the limit. > > There should be some way how to change the limit. Might be worth to
explore if it could be through stream context option.
> > - Raise a warning and return false, instead of throwing an exception. > This is how stream functions currently work. It is not pretty, but it is > consistent. > >
It should use the new stream errors that got just approved and will get soon merged. Kind regards, Jakub

Sjoerd Langkemper

102 days ago
On Thu, May 21, 2026, at 22:59, Jakub Zelenka wrote:
> Might be worth to explore if it could be through stream context option.
Yes, this is an excellent idea. I like this better than a PHP INI setting. I changed the PR and the RFC to include a stream context option. https://wiki.php.net/rfc/limit-maximum-number-of-filter-chains https://github.com/php/php-src/pull/22110 Regards, Sjoerd Langkemper

Sjoerd Langkemper

91 days ago
I am starting the vote on this RFC next Friday, 5 June 2026. The RFC suggests limiting the maximum number of filters in php://filter URLs, for security reasons. The latest change is that the maximum number of filters can be configured through a stream context option. RFC: https://wiki.php.net/rfc/limit-maximum-number-of-filter-chains Discussion thread: https://news-web.php.net/php.internals/130813 Pull request: https://github.com/php/php-src/pull/22110 Regards, Sjoerd Langkemper

Tim Düsterhus

91 days ago
Hi Am 2026-06-02 14:18, schrieb Sjoerd Langkemper:
> I am starting the vote on this RFC next Friday, 5 June 2026. The RFC > suggests limiting the maximum number of filters in php://filter URLs, > for security reasons. The latest change is that the maximum number of > filters can be configured through a stream context option. > > RFC: https://wiki.php.net/rfc/limit-maximum-number-of-filter-chains > Discussion thread: https://news-web.php.net/php.internals/130813 > Pull request: https://github.com/php/php-src/pull/22110
Can you please clean out the “Open Issues” and add an example as to how the `filter.max_filter_count` context option would be used (i.e. what folks would need to write if they wanted to modify the default value) and possibly also one showing how `stream_filter_append()` can be used to programmatically add filters to avoid the limit? Please also fix the title of the voting widget, it says “Implement $feature as outlined in the RFC?”. For completeness it would also be helpful if the “Backward Incompatible Changes” section would mention if the new context option is forward compatible (i.e. whether it's silently ignored in existing PHP versions). I'm generally in favor of the proposal, but want to see the examples to make an educated decision. These will also be helpful for the upgrading guide if / when the RFC is accepted. Best regards Tim Düsterhus

Sjoerd Langkemper

91 days ago
Thank you for your feedback. I made the changes you requested to the RFC. Changing the global limit with the context option looks like this: stream_context_set_default(['filter' => ['max_filter_count' => 123]]); And setting this (or any other non-existing option) has no effect on PHP versions that don't support it. Regards, Sjoerd Langkemper

Tim Düsterhus

90 days ago
Hi Am 2026-06-02 16:13, schrieb Sjoerd Langkemper:
> Thank you for your feedback. I made the changes you requested to the > RFC. Changing the global limit with the context option looks like this: > > stream_context_set_default(['filter' => ['max_filter_count' => 123]]); > > And setting this (or any other non-existing option) has no effect on > PHP versions that don't support it.
Thank you, this is helpful. It previously wasn't clear to me from the RFC that the `.` in the context option name was meant to indicate nesting. I've given the RFC another read and I don't have further comments. The proposal makes sense and 16 seems to be a reasonable default. Best regards Tim Düsterhus

Sjoerd Langkemper

88 days ago
Please vote on the RFC "Limit maximum number of filter chains". A single yes/no vote is requested to approve of the limit on filter chains as proposed in the RFC. I opened the vote just now and it closes in three weeks on 26 June 2026. RFC: https://wiki.php.net/rfc/limit-maximum-number-of-filter-chains Discussion: https://news-web.php.net/php.internals/130813 Regards, Sjoerd Langkemper

Marco Pivetta

88 days ago
Hey Sjoerd, On Fri, 5 Jun 2026 at 09:19, Sjoerd Langkemper <sjoerd-php@linuxonly.nl> wrote:
> Please vote on the RFC "Limit maximum number of filter chains". A single > yes/no vote is requested to approve of the limit on filter chains as > proposed in the RFC. I opened the vote just now and it closes in three > weeks on 26 June 2026. > > RFC: https://wiki.php.net/rfc/limit-maximum-number-of-filter-chains > Discussion: https://news-web.php.net/php.internals/130813 > > Regards, > > Sjoerd Langkemper > >
I voted "yes" due to this adding a limitation. If it were for me, all this stuff should be deprecated/removed: I actually learned that horrors like these are possible in PHP's streaming API from your RFC, and now I kinda wish I didn't know... Marco Pivetta https://mastodon.social/@ocramius https://ocramius.github.io/

Tim Düsterhus

82 days ago
Hi Am 2026-06-05 09:18, schrieb Sjoerd Langkemper:
> Please vote on the RFC "Limit maximum number of filter chains". A > single yes/no vote is requested to approve of the limit on filter > chains as proposed in the RFC. I opened the vote just now and it closes > in three weeks on 26 June 2026. > > RFC: https://wiki.php.net/rfc/limit-maximum-number-of-filter-chains > Discussion: https://news-web.php.net/php.internals/130813
One note regarding policy for future RFCs: The vote should get a dedicated top-level thread, so that it arrives “freshly” in everyone’s inbox. Just changing the Subject in-thread is not always easy to see; I only noticed voting just now. Other than that, I happily voted in favor. Best regards Tim Düsterhus

Sjoerd Langkemper

67 days ago
On Fri, Jun 5, 2026, at 09:18, Sjoerd Langkemper wrote:
> Please vote on the RFC "Limit maximum number of filter chains".
The vote is closed and the RFC is accepted. 30 yes, 0 no, 2 abstain. Thank you for voting. RFC: https://wiki.php.net/rfc/limit-maximum-number-of-filter-chains Implementation: https://github.com/php/php-src/pull/22110 Regards, Sjoerd