Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:128395 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by lists.php.net (Postfix) with ESMTPS id 487831A00BC for ; Tue, 5 Aug 2025 09:15:20 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1754385219; bh=xI/xbgzckI4AzV8u+Euf6uk1lmEBGDu4uBLg8vIazuk=; h=Date:From:To:Subject:From; b=Hz6/v2oKHp2QsV9iYSHxaqt02HPC295JrAVURYyuJ7ZUaKqqJWFJA/vwd7eiN+EG2 /odzj2CMF4QHrxs8kL7OFbSGkbWzeWG960bBHYfrq92FJqf5C3nvfxLWNJLWvp8Ae+ iPPMowNM/gpN8VoTpmHaAxPyxakPJN769uFl5XmQ39ysw6p/NFwjbVf6ipWkljFwSR 1KCKv69WyNcphmmRI8RUJE02Xz+ZpjlhS+ET+3pSL8/kUeIr9XRi82EIW+n/lBh5js AI1orcdCEbWKyW4KlQJdKhN9i6V570udO6sy0Vb2NmqUnTZ2QCEiYnuKPWs45DuxV0 Fsw4CUg4WFOrw== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 2035E1801EE for ; Tue, 5 Aug 2025 09:13:39 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on php-smtp4.php.net X-Spam-Level: * X-Spam-Status: No, score=1.3 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DMARC_NONE,HTML_MESSAGE,RCVD_IN_DNSWL_LOW,SPF_HELO_PASS, SPF_SOFTFAIL autolearn=no autolearn_force=no version=4.0.1 X-Spam-Virus: No X-Envelope-From: Received: from fout-b2-smtp.messagingengine.com (fout-b2-smtp.messagingengine.com [202.12.124.145]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Tue, 5 Aug 2025 09:13:28 +0000 (UTC) Received: from phl-compute-04.internal (phl-compute-04.phl.internal [10.202.2.44]) by mailfout.stl.internal (Postfix) with ESMTP id 81CB91D0011D for ; Tue, 5 Aug 2025 05:15:08 -0400 (EDT) Received: from phl-imap-08 ([10.202.2.84]) by phl-compute-04.internal (MEProxy); Tue, 05 Aug 2025 05:15:08 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:message-id :mime-version:reply-to:subject:subject:to:to:x-me-proxy :x-me-sender:x-me-sender:x-sasl-enc; s=fm3; t=1754385308; x= 1754471708; bh=4BXFBDxLIj38KcyDIlsVWfYfsivcieOBxjsNTN2o/sY=; b=H trO/g5D47KGjmGWo11n3P93SXRF3lmPL5DeQ/buvU63vFBccE3C+PJ6qmSI8rmXK EsctWkTnQVQdbjHLvOm1k5750kpjvwAIGCDnRnamFzE9l4/rabu+c65KcsbUtKHk o17qkxeV4KzCsRJ2yJVdwM/LEOksiXz9mWNJ5ZuOaS6w+uIuA1w7ky4rKGSM4kk6 UjbDmp427N6WoK5UK8SvN8HBFju8jaAwmQLRUg5UxuQR3MRRYi1UJicH2dFwDV/n NHinEyrnua9bikuUsLthM4ODn5ll17T+HQJ8nnXVMdP0/w8ON/mR+VYKoQvm7lSm 9T9kuZ/EHlx6SXOOoOFug== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdefgdduudegjeelucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucenucfjughrpefoggffhffvkffutgesrgdtreerredttd enucfhrhhomhepfdevrghsphgvrhcunfgrnhhgvghmvghijhgvrhdfuceolhgrnhhgvghm vghijhgvrhesphhhphdrnhgvtheqnecuggftrfgrthhtvghrnhepveevvdejudehffevgf fhgedvieefgeehffdvhfegvdeggffgleeitdfhveefgefhnecuffhomhgrihhnpehofigr shhprdhorhhgpdhgihhthhhusgdrtghomhenucevlhhushhtvghrufhiiigvpedtnecurf grrhgrmhepmhgrihhlfhhrohhmpehlrghnghgvmhgvihhjvghrsehphhhprdhnvghtpdhn sggprhgtphhtthhopedupdhmohguvgepshhmthhpohhuthdprhgtphhtthhopehinhhtvg hrnhgrlhhssehlihhsthhsrdhphhhprdhnvght X-ME-Proxy: Feedback-ID: id4f946ef:Fastmail Received: by mailuser.phl.internal (Postfix, from userid 501) id 22A672CE0071; Tue, 5 Aug 2025 05:15:08 -0400 (EDT) X-Mailer: MessagingEngine.com Webmail Interface Precedence: list list-help: list-post: List-Id: x-ms-reactions: disallow MIME-Version: 1.0 Date: Tue, 05 Aug 2025 11:14:26 +0200 To: internals@lists.php.net Message-ID: <929642ac-2628-4bdb-acbd-7268231a6e41@app.fastmail.com> Subject: [PHP-DEV] allowed_classes_callback option for unserialize() Content-Type: multipart/alternative; boundary=fcc5eaa9933e46018c19d392fdba3ee2 From: langemeijer@php.net ("Casper Langemeijer") --fcc5eaa9933e46018c19d392fdba3ee2 Content-Type: text/plain Content-Transfer-Encoding: 7bit I'm writing a this email to propose adding allowed_classes_callback option to unserialize(). The class name as parsed from the serialized string is passed as a parameter to the callback, it should return a boolean. true would allow the class, false would block it. This blocks classes the same way 'allowed_classes' would, but by callback instead. The callback will be triggered *after* allowed_classes is evaluated (if present). Blocking will have the same effect as allowed_classes, using __PHP_Incomplete_Class. This callback would solve a few problems where allowed_classes is not sufficient: - This would also allow for fixing legacy applications where it is not exactly clear what is being unserialized. In my use-case the callable returns a true value but a E_USER_DEPRECATION is triggered. This way data can be collected about what classes to allow through monitoring these deprecations, providing a non-disrupting way to secure unserialize calls. This is especially helpful in very generic unserialize usages like caches. - It would allow for an is_subclass_of() check where for example an interface can be added to classes that are safe to get unserialized. Current allowed_classes array only matches the exact class, not it's children. Note that these problems are not resolvable by using unserialize_callback_func because that call only happens for unloaded classes, where the PHP Object Injection vulnerability https://owasp.org/www-community/vulnerabilities/PHP_Object_Injection affects classes that might already have been loaded. In de pull request Jakub wrote: > Personally I don't see any issue here and it seems quite self contained and small so I wouldn't mind to get it merged without RFC. It might be worth to email internals first to double check that there are no objections first. Out of scope: I understand that __PHP_Incomplete_Class is not very 2025. Adding an option throw_for_unknown_classes has been suggested on this list. I'm happy to implement that, but for now I'd like to keep it at allowed_classes_callback. Feedback is very much appreciated. The change and some more details live here: https://github.com/php/php-src/pull/19087 --fcc5eaa9933e46018c19d392fdba3ee2 Content-Type: text/html Content-Transfer-Encoding: quoted-printable
I'm writing = a this email to propose adding allowed_classes_callback option to unseri= alize(). 

The class name as parsed from th= e serialized string is passed as a parameter to the callback, it should = return a boolean. true would allow the class, false would block it. This= blocks classes the same way 'allowed_classes' would, but by callback in= stead. The callback will be triggered *after* allowed_classes is evaluat= ed (if present). Blocking will have the same effect as allowed_classes, = using __PHP_Incomplete_Class.

This callback wou= ld solve a few problems where allowed_classes is not sufficient:

- This would also allow for fixing legacy applications= where it is not exactly clear what is being unserialized. In my use-cas= e the callable returns a true value but a E_USER_DEPRECATION is triggere= d. This way data can be collected about what classes to allow through mo= nitoring these deprecations, providing a non-disrupting way to secure un= serialize calls. This is especially helpful in very generic unserialize = usages like caches.

- It would allow for an is_= subclass_of() check where for example an interface can be added to class= es that are safe to get unserialized. Current allowed_classes array only= matches the exact class, not it's children. 

<= div>Note that these problems are not resolvable by using unserialize_cal= lback_func because that call only happens for unloaded classes, where th= e PHP Object Injection vulnerability https://owasp.org/www-comm= unity/vulnerabilities/PHP_Object_Injection affects classes that migh= t already have been loaded.

In de pull request = Jakub wrote:
Personally I don't see = any issue here and it seems quite self contained and small so I wouldn't= mind to get it merged without RFC. It might be worth to email internals= first to double check that there are no objections first.

Out of scope: I understand that __PHP_Incomplet= e_Class is not very 2025. Adding an option throw_for_unknown_classes has= been suggested on this list. I'm happy to implement that, but for now I= 'd like to keep it at allowed_classes_callback.

Feedback is very much appreciated.

The change = and some more details live here: https://github.com/php/php-src/pull/19087
<= br>

--fcc5eaa9933e46018c19d392fdba3ee2--