[wp-trac] [WordPress Trac] #62970: Adding Defense-in-Depth against PHP Object Injection

WordPress Trac noreply at wordpress.org
Sat Feb 15 17:56:15 UTC 2025


#62970: Adding Defense-in-Depth against PHP Object Injection
-------------------------------+-----------------------------
 Reporter:  FrancescoCarlucci  |      Owner:  (none)
     Type:  enhancement        |     Status:  new
 Priority:  normal             |  Milestone:  Awaiting Review
Component:  Security           |    Version:
 Severity:  normal             |   Keywords:
  Focuses:                     |
-------------------------------+-----------------------------
 The WordPress core function maybe_unserialize() uses PHP unserialization
 in an insecure way, and I think we are all well aware of that.

 The reason is that PHP unserialize automatically instantiates any object
 received as input, and if the object uses magic methods it can lead to
 RCE, file deletion and other dangerous security issues. In the last year
 there were hundreds of security vulnerabilities caused by unsafe use of
 maybe_unserialize(), and with this ticket I want to propose a fix at core
 level, which in my opinion is easy straightforward :)

 PHP unserialize() accepts a second parameter `allowed_classes` that can be
 set to false to disallow automatic class instantiation, or can be
 restricted to specific classes.

 So we can tweak maybe_unserialize() this way:

 {{{#!php
 <?php
 function maybe_unserialize( $data ) {
 if ( is_serialized( $data ) ) { // Don't attempt to unserialize data that
 wasn't serialized going in.
   $allowed_classes = apply_filters( 'maybe_unserialize_allowed_classes',
 false );
   return @unserialize( trim( $data ), [ 'allowed_classes' =>
 $allowed_classes ] );
 }

 return $data;
 }
 }}}

 This way allowed_classes is false by default and can be hooked to true or
 a set of classes, this would be a security-first approach.

 Other ways, we can have a backward-compatibility first approach:

 {{{#!php
 <?php
 function maybe_unserialize( $data ) {
 if ( is_serialized( $data ) ) { // Don't attempt to unserialize data that
 wasn't serialized going in.
   $allowed_classes = apply_filters( 'maybe_unserialize_allowed_classes',
 true );
   return @unserialize( trim( $data ), [ 'allowed_classes' =>
 $allowed_classes ] );
 }

 return $data;
 }
 }}}

 And then site owners can harden their WP instance with this one-liner:

 {{{#!php
 <?PHP

 add_filter( 'maybe_unserialize_allowed_classes', '__return_false' );

 }}}

 To restrict instantiations to specific classes, plugins could hook in like
 this:

 {{{#!php
 <?php
 add_filter( 'maybe_unserialize_allowed_classes', function() {
     return ['MyClass', 'AnotherClass', 'SomeOtherClass'];
 });

 }}}

 Anything else needed to merge it in the core? Does anyone see anything
 that could go wrong?

 Thanks,
 Francesco

-- 
Ticket URL: <https://core.trac.wordpress.org/ticket/62970>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform


More information about the wp-trac mailing list