Repository navigation
ext/filter: Fix FILTER_VALIDATE_IP range flag bypasses through IPv6 notations - #25426
Open
xavierleune wants to merge 3 commits into
Open
xavierleune wants to merge 3 commits into
xavierleune wants to merge 3 commits into
Conversation
_php_filter_validate_ipv6() filled ip[] as ::ffff:a.b.c.d for every address with a dotted IPv4 suffix, discarding the hexadecimal prefix, whose blocks were also stored two positions off. The range flags were then checked against the wrong address, so fc00::1.2.3.4 passed FILTER_FLAG_NO_PRIV_RANGE while fc00::102:304 did not. With an IPv4 suffix, the hexadecimal part is now limited to 6 blocks, stored at their real index, and the IPv4 part goes to ip[6] and ip[7]. Validation itself is unchanged. The deprecated IPv4-compatible block ::/96 is marked reserved, so that ::a.b.c.d keeps being rejected by FILTER_FLAG_GLOBAL_RANGE and FILTER_FLAG_NO_RES_RANGE. Also, only 64:ff9b::/96 (RFC 6052) is global, not the whole 64:ff9b::/32: the local-use prefix 64:ff9b:1::/48 (RFC 8215) is not globally reachable.
Some IPv6 addresses lead to the IPv4 address they embed: IPv4-mapped addresses through dual-stack sockets, NAT64 addresses through a NAT64 gateway. The range flags never looked at that IPv4 address, so ::ffff:10.0.0.1 passed FILTER_FLAG_NO_PRIV_RANGE and 64:ff9b::a9fe:a9fe (169.254.169.254) passed every flag. - 64:ff9b::/96 is classified as its embedded IPv4 address (RFC 6052 3.1: the well-known prefix must not embed non-global IPv4 addresses). - 6to4 (2002::/16) stays non-global, and is private or reserved if its embedded IPv4 address is. - IPv4-mapped, IPv4-compatible and IPv4-translated (::ffff:0:0:0/96) addresses are reserved, and private if their IPv4 address is. - The position of the IPv4 address in 64:ff9b:1::/48 (RFC 8215) depends on the operator, so the local-use prefix is private. This completes the fix of bug #61700, whose report expected ::ffff:192.168.1.1 to fail FILTER_FLAG_NO_PRIV_RANGE. phpGH-7476 only kept the existing classification: "Note that this patch does not fix what we consider as reserved and private, respectively, but merely tries to keep what we had so far."
fec0::1 passed FILTER_FLAG_NO_PRIV_RANGE, and ff02::1 and 224.0.0.1 passed FILTER_FLAG_NO_RES_RANGE. - fec0::/10 (RFC 3879, deprecated site-local, still used on internal networks) is private. - ff00::/8 and 224.0.0.0/4 (multicast) are reserved. - 3fff::/20 (RFC 9637, documentation) and 5f00::/16 (RFC 9602, SRv6 SIDs) are not global. filter_ipv4_rfc6890.phpt used 224.0.0.0 as the lower bound of 240.0.0.0/4; 224.0.0.0/4 now has its own case.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Target: PHP-8.4
FILTER_FLAG_NO_PRIV_RANGE,FILTER_FLAG_NO_RES_RANGEandFILTER_FLAG_GLOBAL_RANGEare commonly used to keep user-supplied addresses away from internal networks (SSRF), but several IPv6 notations of internal addresses pass them. These are "Low" issues under the security policy, hence fixed as regular bugs. Almost all changes make the flags reject more addresses, which cannot open a bypass.Commits
X::a.b.c.das::ffff:a.b.c.d, discarding the hexadecimal prefix. Blocks are now stored at their real index; validation itself is unchanged.::/96is marked reserved so that::a.b.c.dstays rejected, and only64:ff9b::/96is global, not the whole/32.64:ff9b::/96: classified as the IPv4 address itself (RFC 6052 §3.1).::ffff:0:0:0/96): reserved, and private if the IPv4 address is.2002::/16: still non-global, private or reserved if the IPv4 address is.64:ff9b:1::/48(RFC 8215): private, since the IPv4 position depends on the operator.fec0::/10site-local (deprecated, still used internally)ff00::/8,224.0.0.0/4multicast3fff::/20documentation5f00::/16SRv6 SIDsBehaviour changes
origin/PHP-8.4→ this branch. ✅ accepted, ❌ rejected.fc00::1.2.3.42606:4700::8.8.8.8,64:ff9b::8.8.8.82001:db8::1.2.3.464:ff9b::10.0.0.164:ff9b::a00:164:ff9b::a9fe:a9fe,64:ff9b::7f00:164:ff9b:1::a00:12002:a00:1::(6to4 of 10.0.0.1)2002:7f00:1::(6to4 of 127.0.0.1)::ffff:10.0.0.1,::10.0.0.1,::ffff:0:10.0.0.1::7f00:1,::808:808,::ffff:0:7f00:11::127.0.0.1,::1:127.0.0.1fec0::1ff02::1,224.0.0.13fff::1,5f00::1Addresses that become accepted are either dotted forms now classified like their hexadecimal form, which already passed, or actually global addresses. None leads to an internal network. CGNAT (
100.64.0.0/10) is intentionally unchanged.Notes
bug61700.phptexpected::ffff:192.168.1.1to passNO_PRIV_RANGE, the opposite of what #61700 reported and requinix confirmed. Fix #61700: FILTER_FLAG_IPV6/FILTER_FLAG_NO_PRIV|RES_RANGE failing #7476 was a refactoring that, per its description, "does not fix what we consider as reserved and private, respectively, but merely tries to keep what we had so far". This PR completes #61700.bug77221.phpt:64:ff9b::and64:ff9b::ffffembed0.0.0.0/8and are no longer global; two really global addresses were added.filter_ipv4_rfc6890.phpt:224.0.0.0/4gets its own case.inet_pton()hex form (298,181 mismatches before).