Re: [RFC] Vetting cURL connections with CURLOPT_OPENSOCKETFUNCTION

From: Date: Thu, 12 Nov 2026 12:47:07 +0000
Subject: Re: [RFC] Vetting cURL connections with CURLOPT_OPENSOCKETFUNCTION
References: 1 2  Groups: php.internals 
Request: Send a blank email to internals+get-133087@lists.php.net to get a copy of this message
Hi Nicolas, Le jeu. 12 nov. 2026 à 11:57, Nicolas Grekas <nicolas.grekas+php@gmail..com> a écrit : > 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,kƒ >> ÖM´¤`ó~|pG" >> 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 > > > > > Thanks for your feedback. I dropped the enum, you're right the new api is more consistent with other curl callbacks. PR, RFC and polyfill are up to date, Xavier

« previous php.internals (#133087) next »