Re: [RFC] Vetting cURL connections with CURLOPT_OPENSOCKETFUNCTION

From: Date: Thu, 12 Nov 2026 10:56:49 +0000
Subject: Re: [RFC] Vetting cURL connections with CURLOPT_OPENSOCKETFUNCTION
References: 1  Groups: php.internals 
Request: Send a blank email to internals+get-133086@lists.php.net to get a copy of this message
Hi Xavier, thanks for the RFC Le jeu. 12 nov. 2026 à 11:45, Xavier Leune <xavier.leune+internals@gmail.com> a écrit : > Hi internals, > > Following the "SSRF protection in curl" thread, I'd like to start the > discussion on this RFC: > https://wiki.php.net/rfc/curl_opensocketfunction > > It adds CURLOPT_OPENSOCKETFUNCTION to ext/curl, with libcurl's name and > value. The callback is invoked after DNS resolution and before connect(), > once for each socket libcurl is about to create. It returns a bool that > allows or refuses the connection: > > curl_setopt($ch, CURLOPT_OPENSOCKETFUNCTION, > function (CurlHandle $handle, CurlAddressFamily $family, string > $address, ?int $port): bool { > // Only publicly routable addresses. > return $family !== CurlAddressFamily::Unix > && filter_var($address, FILTER_VALIDATE_IP, > FILTER_FLAG_GLOBAL_RANGE) !== false; > }); > > This is the point where an SSRF policy can be enforced reliably. It > applies to the address actually about to be contacted, on every redirect > hop and for every address libcurl tries, whatever spelling the URL used and > whatever DNS answered. > > Compared to the earlier proposals: > > - It returns a bool, as Sjoerd suggested in the thread. No socket is > exposed to userland, and ext/curl gains no dependency on ext/sockets. > - It keeps libcurl's option name, rather than a PHP-specific one. > - The handle comes first, as in the other ext/curl callbacks. A new > CurlAddressFamily enum (IpV4, IpV6, Unix) tells a policy whether $address > is an IP or a socket path. $port is null for UNIX sockets. > - The return type can later be widened, and parameters appended, without > breaking existing callbacks. > > The implementation is in > https://github.com/php/php-src/pull/25414, which > supersedes GH-23657. > > A polyfill, curl_hardening (https://github.com/xavierleune/curl-hardening), > implements the same API for PHP 8.3 to 8.6. It stays inert on a PHP that > provides the option natively. > > As discussed with Levi, streams are left to a separate RFC (see Future > Scope). > > Thanks to everyone who took part in the previous thread and reviewed the > PRs. Feedback is welcome. > > Regards, > Xavier > As I put on the PR, I'd drop the enum and use an int port: function (CurlHandle $handle, string $address, int $port): bool The address already tells IPv4, IPv6 and UNIX sockets apart, and filter_var() rejects a socket path anyway. The enum can even mislead: ::ffff:127.0.0.1 is reported as IpV6. For UNIX sockets, port 0 is what CURLOPT_PREREQFUNCTION and CURLINFO_PRIMARY_PORT already report/. Using null would be inconsistent and thus surprizing. Cheers, Nicolas

« previous php.internals (#133086) next »