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
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
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
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
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
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
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
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
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
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
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
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/130813Regards,
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
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