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
I'm writing a this email to propose adding allowed_classes_callback
option tounserialize()
.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
I am not opposed to this, but I would like to see it go through an RFC.
--Larry Garfield