Verifica se una stringa è serializzata?


Risposte:


191

Direi, provaci unserialize;-)

Citando il manuale:

Nel caso in cui la stringa passata non sia non serializzabile, viene restituito FALSE e viene emesso E_NOTICE.

Quindi, devi controllare se il valore restituito è falseo meno (con ===o !==, per essere sicuro di non avere alcun problema con 0o nullo qualsiasi cosa uguale a false, direi) .

Basta fare attenzione all'avviso: potrebbe essere necessario / necessario utilizzare l' operatore @ .

Per esempio :

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

Ti porterà:

not ok


EDIT: Oh, e come ha detto @Peter (grazie a lui!), Potresti avere dei problemi se stai cercando di annullare la serializzazione della rappresentazione di un falso booleano :-(

Pertanto, b:0;potrebbe essere utile verificare che la stringa serializzata non sia uguale a " "; qualcosa del genere dovrebbe fare il trucco, suppongo:

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

testare quel caso speciale prima di provare a non serializzare sarebbe un'ottimizzazione - ma probabilmente non è così utile, se non hai spesso un falso valore serializzato.


20
Ma cosa succede se il valore non serializzato è un valore booleano con un valore FALSO?
Peter,

1
@Peter: eccellente osservazione; Ho modificato la mia risposta con una proposta per affrontare quel caso; Grazie !
Pascal MARTIN,

Grazie. :) Supponevo che questa sarebbe stata probabilmente la risposta .. Mi sembra che ci dovrebbe essere un modo per scoprire se è serializzato prima di forzare effettivamente il parser a tentare di elaborarlo.
Dang

1
Questo metodo ha un impatto ragionevole sulle prestazioni con grandi quantità di dati?
pie6k,

2
IMPORTANTE: mai sottovalutare mai i dati utente non elaborati poiché possono essere utilizzati come vettore di attacco. OWASP: PHP_Object_Injection
ArtBIT

56

Non ho scritto questo codice, in realtà è di WordPress. Ho pensato di includerlo per chiunque fosse interessato, potrebbe essere eccessivo ma funziona :)

<?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;
}

1
Fondamentalmente avevo bisogno di un regex per fare un rilevamento di base, ho finito per usare:^([adObis]:|N;)
farinspace

5
L'attuale versione di WordPress è un po 'più sofisticata: codex.wordpress.org/Function_Reference/…
ChrisV

3
+1 per dare crediti. Non sapevo che WordPress avesse questo built-in. Grazie per l'idea: ora vado avanti e creerò un archivio di funzioni utili da WordPress Core.
Amal Murali,

Ultimo riferimento alla funzione URL per wordpress: developer.wordpress.org/reference/functions/is_serialized
Cédric Françoys

18

Ottimizzare la risposta di Pascal MARTIN

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

16

Se la stringa $ è un falsevalore serializzato , ovvero la funzione di $string = 'b:0;' SoN9ne ritorna false, è errata

quindi la funzione sarebbe

/**
 * Check if a string is serialized
 *
 * @param string $string
 *
 * @return bool
 */
function is_serialized_string($string)
{
    return ($string == 'b:0;' || @unserialize($string) !== false);
}

2
Scambiare l'ordine di questi test sarebbe più efficiente.
artfulrobot

@ (Presso l'operatore) dovrebbe essere scoraggiato. Utilizzare invece try catch block.
Francisco Luz,

@FranciscoLuz dal manuale php.net/manual/en/function.unserialize.php In case the passed string is not unserializeable, FALSE is returned and E_NOTICE is issued. Non è possibile rilevare l' errore E_NOTICE in quanto non costituisce un'eccezione.
Hazem Noor,

@HazemNoor L'ho provato con PHP 7 e viene catturato. Inoltre, in PHP 7, c'è un catch (\ Throwable $ e) che cattura tutto ciò che va storto sotto il cofano.
Francisco Luz,

@FranciscoLuz come hai catturato E_Notice in PHP 7?
user427969,

13

Nonostante l'eccellente risposta di Pascal MARTIN, ero curioso di poter affrontare questo in un altro modo, quindi l'ho fatto solo come esercizio mentale

<?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 );
}

E funziona davvero. L'unica avvertenza è che probabilmente si romperà se si dispone di un gestore degli errori registrato a causa del funzionamento di $ php_errormsg .


1
+1: Questo è divertente, devo ammetterlo - non ci avrei pensato! E non trovo il modo di farlo fallire troppo ^^ Bel lavoro! E grazie per il commento sulla mia risposta: senza di essa, probabilmente non avrei visto questa risposta.
Pascal MARTIN,

$ a = 'bla'; $ b = 'b: 0;'; Prova a non serializzare $ a quindi $ b con questo, entrambi falliranno mentre $ b non dovrebbe.
bardiir,

Non se ci fosse un errore proprio prima. Poiché $ php_errormsg conterrà ancora l'errore di serializzazione di prima e una volta che si deserializza false, allora fallirà.
bardiir,

Sì, ma solo se non si verifica l'errore tra la deserializzazione $ae la deserializzazione $b, che non è una progettazione pratica dell'applicazione.
Peter Bailey

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

Gestisce correttamente il caso di serialize(false). :)


3

incorporare una funzione

function isSerialized($value)
{
   return preg_match('^([adObis]:|N;)^', $value);
}

1
Questa regex è pericolosa, sta tornando positiva quando a:(o b:ecc.) È presente da qualche parte all'interno di $ value, non all'inizio. E ^qui non significa l'inizio di una stringa. È totalmente fuorviante.
Denis Chmel,

3

C'è la soluzione WordPress: (i dettagli sono qui)

    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;
    }

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;
}

}

5
bene, questo sarebbe vero anche per molte stringhe JSON, no? Quindi non è affidabile determinare se la stringa può essere annullata / serializzata.
Gordon,

Potrebbe essere vero, ma se l'alternativa è serializzata, o solo testo semplice, come è stato per me, funziona come un fascino.
Björn3,

1
@ Björn3 "Beh, funziona in questo caso specifico per me" è una pessima mentalità da avere durante la programmazione. Ci sono molti sviluppatori che sono pigri o che non pensano al futuro in questo modo e in seguito diventa un incubo quando altri sviluppatori devono lavorare con il loro codice o provare a cambiare qualcosa e all'improvviso nulla funziona più correttamente.
BadHorsie,

Rendere il codice completamente solido (se ciò fosse possibile) non è sempre l'obiettivo o la migliore pratica. Non quando si tratta di una spesa di tempo. Questo è vero solo dal punto di vista dei programmatori. Nella vita reale ci sono molte circostanze in cui il modo preferito è veloce e sporco.
Björn3,

1

Questo funziona bene per me

<?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));
    }

?>

Si prega di tenere presente che controlla se la stringa data è una stringa dall'aspetto serializzato - in realtà non verificherà la validità di quella stringa.
Eithed

-2

Preferisco farlo in questo modo:

 if (is_array(unserialize($serialized_string))):

Perché la variabile serializzata dovrebbe essere un array? Può davvero essere di qualunque tipo.
Valerio Bozz,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.