# Hardening PHP against filter chain attacks

- Thread: https://externals.io/message/130813
- Original archive: https://news-web.php.net/php.internals/130813
- Started by Sjoerd Langkemper on 2026-05-09T13:16:57+00:00
- Messages: 14
- RFC: Limit maximum number of filter chains (Implemented (PHP 8.6)) — https://externals.io/rfc/limit-maximum-number-of-filter-chains.md
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · RFCs at https://externals.io/rfc.md · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #130813 — Sjoerd Langkemper — 2026-05-09T13:16:57+00:00

URL: https://externals.io/message/130813#130813

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

---

## #130863 — Derick Rethans — 2026-05-13T10:41:16+00:00

URL: https://externals.io/message/130813#130863 · In reply to: #130813

> 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

---

## #130864 — jvoisin — 2026-05-13T12:16:18+00:00

URL: https://externals.io/message/130813#130864 · In reply to: #130863

> 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.


> > […]
> 
> 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

---

## #130959 — Sjoerd Langkemper — 2026-05-19T10:58:59+00:00

URL: https://externals.io/message/130813#130959 · In reply to: #130813

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

---

## #130983 — Jakub Zelenka — 2026-05-21T20:59:19+00:00

URL: https://externals.io/message/130813#130983 · In reply to: #130959

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

---

## #130987 — Sjoerd Langkemper — 2026-05-22T12:05:30+00:00

URL: https://externals.io/message/130813#130987 · In reply to: #130983

> 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

---

## #131103 — Sjoerd Langkemper — 2026-06-02T12:18:19+00:00

URL: https://externals.io/message/130813#131103 · In reply to: #130813

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

---

## #131105 — tim@bastelstu.be — 2026-06-02T12:50:55+00:00

URL: https://externals.io/message/130813#131105 · In reply to: #131103

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

---

## #131108 — Sjoerd Langkemper — 2026-06-02T14:13:20+00:00

URL: https://externals.io/message/130813#131108 · In reply to: #131105

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

---

## #131119 — tim@bastelstu.be — 2026-06-03T09:10:30+00:00

URL: https://externals.io/message/130813#131119 · In reply to: #131108

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

---

## #131152 — Sjoerd Langkemper — 2026-06-05T07:18:33+00:00

URL: https://externals.io/message/130813#131152 · In reply to: #131103

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

---

## #131153 — Marco Pivetta — 2026-06-05T09:07:18+00:00

URL: https://externals.io/message/130813#131153 · In reply to: #131152

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/

---

## #131204 — tim@bastelstu.be — 2026-06-11T13:01:21+00:00

URL: https://externals.io/message/130813#131204 · In reply to: #131152

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

---

## #131539 — Sjoerd Langkemper — 2026-06-26T15:11:06+00:00

URL: https://externals.io/message/130813#131539 · In reply to: #131152

> 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
