¿Verifica si una cadena está serializada?

Respuestas:

191

Yo diría, inténtalo unserialize;-)

Citando el manual:

En caso de que la cadena pasada no sea no serializable, se devuelve FALSE y se emite E_NOTICE.

Por lo tanto, usted tiene que comprobar si el valor de retorno es falseo no (con ===o !==, para asegurarse de no tener ningún problema 0o nullo cualquier cosa que es igual a false, diría) .

Solo tenga cuidado con el aviso: es posible que desee / necesite usar el operador @ .

Por ejemplo :

$str = 'hjkl';
$data = @unserialize($str);
if ($data !== false) {
    echo "ok";
} else {
    echo "not ok";
}

Te conseguirá:

not ok


EDITAR: Ah, y como dijo @Peter (¡gracias a él!), Podrías tener problemas si intentas deserializar la representación de un falso booleano :-(

Por lo tanto, verificar que su cadena serializada no sea igual a " b:0;" también puede ser útil; algo como esto debería hacer el truco, supongo:

$data = @unserialize($str);
if ($str === 'b:0;' || $data !== false) {
    echo "ok";
} else {
    echo "not ok";
}

probar ese caso especial antes de intentar deserializar sería una optimización, pero probablemente no sea tan útil, si a menudo no tiene un valor serializado falso.

Pascal MARTIN
fuente
20
Pero, ¿qué pasa si el valor no serializado es un valor booleano con un valor FALSO?
Peter
1
@Peter: excelente comentario; He editado mi respuesta con una propuesta para tratar ese caso; Gracias !
Pascal MARTIN
Gracias. :) Supuse que esta probablemente sería la respuesta. Me parece que debería haber una manera de averiguar si está serializado antes de obligar realmente al analizador a intentar procesarlo.
Dang
1
¿Este método tiene un impacto razonable en el rendimiento con datos más grandes?
pie6k
2
IMPORTANTE: nunca deserialice los datos sin procesar del usuario, ya que pueden usarse como un vector de ataque. OWASP: PHP_Object_Injection
ArtBIT
56

No escribí este código, en realidad es de WordPress. Pensé que lo incluiría para cualquier persona interesada, podría ser excesivo pero funciona :)

<?php
function is_serialized( $data ) {
    // if it isn't a string, it isn't serialized
    if ( !is_string( $data ) )
        return false;
    $data = trim( $data );
    if ( 'N;' == $data )
        return true;
    if ( !preg_match( '/^([adObis]):/', $data, $badions ) )
        return false;
    switch ( $badions[1] ) {
        case 'a' :
        case 'O' :
        case 's' :
            if ( preg_match( "/^{$badions[1]}:[0-9]+:.*[;}]\$/s", $data ) )
                return true;
            break;
        case 'b' :
        case 'i' :
        case 'd' :
            if ( preg_match( "/^{$badions[1]}:[0-9.E-]+;\$/", $data ) )
                return true;
            break;
    }
    return false;
}
Brandon Wamboldt
fuente
1
Básicamente necesitaba una expresión regular para hacer una detección básica, terminé usando:^([adObis]:|N;)
farinspace
55
La versión actual de WordPress es algo más sofisticada: codex.wordpress.org/Function_Reference/…
ChrisV
3
+1 por dar créditos. No sabía que WordPress tenía esto incorporado. Gracias por la idea: ahora seguiré adelante y crearé un archivo de funciones útiles desde el núcleo de WordPress.
Amal Murali
Última referencia a la función de URL a WordPress: developer.wordpress.org/reference/functions/is_serialized
Cédric Françoys
18

Optimizando la respuesta de Pascal MARTIN

/**
 * Check if a string is serialized
 * @param string $string
 */
public static function is_serial($string) {
    return (@unserialize($string) !== false);
}
SoN9ne
fuente
16

Si $ string es un falsevalor serializado , es decir , la función de $string = 'b:0;' SoN9ne regresa false, está mal

entonces la función sería

/**
 * Check if a string is serialized
 *
 * @param string $string
 *
 * @return bool
 */
function is_serialized_string($string)
{
    return ($string == 'b:0;' || @unserialize($string) !== false);
}
Hazem Noor
fuente
2
Cambiar el orden de estas pruebas sería más eficiente.
artfulrobot
El @ (en el operador) debe ser desalentado. Utilice try catch block en su lugar.
Francisco Luz
@FranciscoLuz del manual php.net/manual/en/function.unserialize.php In case the passed string is not unserializeable, FALSE is returned and E_NOTICE is issued. No podemos detectar el error E_NOTICE ya que no es una excepción lanzada.
Hazem Noor
@HazemNoor Lo probé con PHP 7 y queda atrapado. Además, en PHP 7, hay catch (\ Throwable $ e) que detecta todo lo que sale mal debajo del capó.
Francisco Luz
@FranciscoLuz, ¿cómo atrapaste a E_Notice en PHP 7?
user427969
13

A pesar de la excelente respuesta de Pascal MARTIN, tenía curiosidad por saber si podría abordar esto de otra manera, así que lo hice solo como un ejercicio mental

<?php

ini_set( 'display_errors', 1 );
ini_set( 'track_errors', 1 );
error_reporting( E_ALL );

$valueToUnserialize = serialize( false );
//$valueToUnserialize = "a"; # uncomment this for another test

$unserialized = @unserialize( $valueToUnserialize );

if ( FALSE === $unserialized && isset( $php_errormsg ) && strpos( $php_errormsg, 'unserialize' ) !== FALSE )
{
  echo 'Value could not be unserialized<br>';
  echo $valueToUnserialize;
} else {
  echo 'Value was unserialized!<br>';
  var_dump( $unserialized );
}

Y en realidad funciona. La única advertencia es que probablemente se romperá si tiene un controlador de errores registrado debido a cómo funciona $ php_errormsg .

Peter Bailey
fuente
1
+1: Tengo que admitir que este es divertido, ¡no lo habría pensado! Y tampoco encuentro una manera de hacerlo fallar ^^ ¡Buen trabajo! Y gracias por el comentario sobre mi respuesta: sin ella, probablemente no hubiera visto esta respuesta.
Pascal MARTIN
$ a = 'bla'; $ b = 'b: 0;'; Intente deserializar $ a luego $ b con esto, ambos fallarán mientras que $ b no debería.
bardiir
No si hubo una falla justo antes. Debido a que $ php_errormsg todavía contendrá el error de serialización anterior y una vez que deserialice false, fallará.
Bardiir
Sí, pero solo si no verifica los errores entre la deserialización $ay la deserialización $b, que no es un diseño de aplicación práctico.
Peter Bailey
11
$data = @unserialize($str);
if($data !== false || $str === 'b:0;')
    echo 'ok';
else
    echo "not ok";

Maneja correctamente el caso de serialize(false). :)

caos
fuente
3

construir en una función

function isSerialized($value)
{
   return preg_match('^([adObis]:|N;)^', $value);
}
RossW
fuente
1
Esta expresión regular es peligrosa, regresa positiva cuando a:(o b:etc.) está presente en algún lugar dentro de $ value, no al principio. Y ^aquí no significa el comienzo de una cadena. Es totalmente engañoso.
Denis Chmel
3

Hay una solución de WordPress: (el detalle está aquí)

    function is_serialized($data, $strict = true)
    {
        // if it isn't a string, it isn't serialized.
        if (!is_string($data)) {
            return false;
        }
        $data = trim($data);
        if ('N;' == $data) {
            return true;
        }
        if (strlen($data) < 4) {
            return false;
        }
        if (':' !== $data[1]) {
            return false;
        }
        if ($strict) {
            $lastc = substr($data, -1);
            if (';' !== $lastc && '}' !== $lastc) {
                return false;
            }
        } else {
            $semicolon = strpos($data, ';');
            $brace = strpos($data, '}');
            // Either ; or } must exist.
            if (false === $semicolon && false === $brace)
                return false;
            // But neither must be in the first X characters.
            if (false !== $semicolon && $semicolon < 3)
                return false;
            if (false !== $brace && $brace < 4)
                return false;
        }
        $token = $data[0];
        switch ($token) {
            case 's' :
                if ($strict) {
                    if ('"' !== substr($data, -2, 1)) {
                        return false;
                    }
                } elseif (false === strpos($data, '"')) {
                    return false;
                }
            // or else fall through
            case 'a' :
            case 'O' :
                return (bool)preg_match("/^{$token}:[0-9]+:/s", $data);
            case 'b' :
            case 'i' :
            case 'd' :
                $end = $strict ? '$' : '';
                return (bool)preg_match("/^{$token}:[0-9.E-]+;$end/", $data);
        }
        return false;
    }
ingenioso
fuente
2
/**
 * some people will look down on this little puppy
 */
function isSerialized($s){
if(
    stristr($s, '{' ) != false &&
    stristr($s, '}' ) != false &&
    stristr($s, ';' ) != false &&
    stristr($s, ':' ) != false
    ){
    return true;
}else{
    return false;
}

}
Björn3
fuente
55
bueno, esto también sería cierto para muchas cadenas JSON, ¿no? Por lo tanto, no es confiable determinar si la cadena puede no ser serializada.
Gordon
Puede ser cierto, pero si la alternativa es serializada, o simplemente texto simple, como lo fue para mí, funciona de maravilla.
Björn3
1
@ Björn3 "Bueno, me funciona en este caso específico" es una mentalidad realmente mala al codificar. Hay muchos desarrolladores que son perezosos o que no tienen una visión de futuro como esta y se convierte en una pesadilla más adelante cuando otros desarrolladores tienen que trabajar con su código o tratar de cambiar algo y, de repente, ya nada funciona correctamente.
BadHorsie
Hacer un código completamente sólido (si eso fuera posible) no siempre es el objetivo o la mejor práctica. No cuando se trata de un gasto de tiempo. Esto solo es cierto desde la perspectiva de los programadores. En la vida real hay muchas circunstancias en las que la forma preferida es rápida y sucia.
Björn3
1

Esto funciona bien para mi

<?php

function is_serialized($data){
    return (is_string($data) && preg_match("#^((N;)|((a|O|s):[0-9]+:.*[;}])|((b|i|d):[0-9.E-]+;))$#um", $data));
    }

?>
Daniel Lichtenberg
fuente
Tenga en cuenta que esto verifica si la cadena dada es una cadena de aspecto serializado; en realidad, no verificará la validez de esa cadena.
Eithed
-2

Prefiero hacerlo de esa manera:

 if (is_array(unserialize($serialized_string))):
degers
fuente
¿Por qué la variable serializada debería ser una matriz? Realmente puede ser de cualquier tipo.
Valerio Bozz