====== PHP RFC: Limit maximum number of filter chains ====== * Version: 0.1 * Date: 2026-05-05 * Author: Sjoerd Langkemper * Status: Draft * Implementation: https://github.com/php/php-src/pull/16699 ===== Introduction ===== This RFC proposes to introduce a limit on the number of filters that can be chained within a php:%%//%%filter URL. The primary goal is to mitigate a class of security vulnerabilities where Local File Inclusion (LFI) is elevated to Remote Code Execution (RCE) via complex, generated filter chains. Currently, an attacker can use a long sequence of convert.iconv.* and convert.base64 filters to craft arbitrary content (like a PHP shell) out of any file on the disk, or even from an empty resource. By limiting the chain length to a reasonable number (e.g., 5 or 10), we can effectively disable these automated exploitation tools while maintaining support for legitimate use cases. ===== Proposal ===== The proposal seeks to limit the number of individual filter components allowed in a single `php:%%//%%filter` string, configured with an INI setting such as `filter.max_chain_depth`. This limit applies specifically to the parsing of php:%%//%%filter URLs (e.g., via fopen, include, file_get_contents). When the limit is exceeded, PHP should emit a Warning and the stream open operation should return false. ==== Examples ==== Still works: var_dump(file_get_contents("php://filter/string.toupper|string.rot13/resource=data://text/plain,hello")); // string(5) "URYYB" Fails: var_dump(file_get_contents("php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|...[48 more].../resource=php://temp")); // Warning: file_get_contents(...): filter chain depth of 50 exceeds the limit of 32 // bool(false) ===== Backward Incompatible Changes ===== This is a backward incompatible change for any application that legitimately chains more filters than the defined limit. A survey of open-source code (Sourcegraph) indicates that the vast majority of php:%%//%%filter usages involve 1 filter, and occasionally 2. Legitimate use cases for 5 or more filters were not found outside of CTF challenges and exploit payloads. A malicious payload typically takes more than 100 filters, so there is a large margin where malicious use is blocked but legitimate use is not. ===== Proposed PHP Version(s) ===== * PHP 8.6 ===== RFC Impact ===== ==== To the Ecosystem ==== Most developers will notice no change. Security scanners and WAFs may be simplified as they can rely on the engine's built-in protection. ==== To Existing Extensions ==== Minimal to none, as this logic resides within the core PHP stream wrapper for php:%%//%%. ==== To SAPIs ==== No specific impact to CLI, FPM, or others. ===== Open Issues ===== * Exact Limit: Should the default be 5, 10, or 20? (Most exploits require 50-100+). * What should the INI name be? filter.max_chain_depth * 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? * Should exceeding the limit throw a ValueError (consistent with modern PHP 8 APIs) or a Warning (consistent with legacy stream handling)? ===== Future Scope ===== * Deprecating the use of strings for complex filter definitions in favor of an Object-Oriented API for stream manipulation. * Considering a default allow_url_include = Off and eventually moving toward disabling allow_url_fopen by default in future major versions. ===== Voting Choices ===== Implement limiting the number of Filter Chains in php:%%//%%filter? Primary Vote requiring a 2/3 majority to accept the RFC: * Yes * No * Abstain ===== Patches and Tests ===== There's already an existing merge request, and I am willing to do some work on this. https://github.com/php/php-src/pull/16699 ===== Implementation ===== After the RFC is implemented, this section should contain: - the version(s) it was merged into - a link to the git commit(s) - a link to the PHP manual entry for the feature ===== References ===== * https://github.com/php/php-src/pull/16699 * https://github.com/php/php-src/issues/10453 * https://discourse.thephp.foundation/t/php-dev-proposal-restrict-the-number-of-filters/799 * https://www.synacktiv.com/publications/php-filter-chains-file-read-from-error-based-oracle * https://phrack.org/issues/72/5 ===== Rejected Features ===== Keep this updated with features that were discussed on the mail lists. ===== Changelog ===== * 0.1 2026-05-05 Initial draft