ตรวจสอบดูว่าสตริงเป็นอนุกรมหรือไม่?


คำตอบ:


193

ฉันจะบอกว่าลองดูunserialize;-)

การอ้างอิงคู่มือ:

ในกรณีที่สตริงที่ส่งผ่านไม่สามารถเข้ารหัสได้ระบบจะส่งคืน FALSE และออก E_NOTICE

ดังนั้นคุณจะต้องตรวจสอบว่าค่าตอบแทนเป็นfalseหรือไม่(มี===หรือ!==เพื่อให้แน่ใจว่าจะไม่ให้มีปัญหาใด ๆ กับ0หรือnullหรือสิ่งที่เท่ากับfalseผมว่า)

เพียงแค่ระวังแจ้งให้ทราบล่วงหน้า: คุณอาจต้องการ / ความจำเป็นที่จะใช้ประกอบการ @

ตัวอย่างเช่น:

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

จะได้รับคุณ:

not ok


แก้ไข: โอ้และเช่นเดียวกับที่ @Peter กล่าว (ขอบคุณเขา!) คุณอาจประสบปัญหาหากคุณกำลังพยายามยกเลิกการแสดงข้อมูลของบูลีนเท็จ :-(

ดังนั้นการตรวจสอบว่าสตริงที่ทำให้เป็นอนุกรมของคุณไม่เท่ากับ " b:0;" อาจเป็นประโยชน์เช่นกัน สิ่งนี้ควรใช้เคล็ดลับฉันคิดว่า:

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

การทดสอบกรณีพิเศษนั้นก่อนที่จะพยายามยกเลิกการเข้ารหัสอาจเป็นการเพิ่มประสิทธิภาพ แต่อาจไม่เป็นประโยชน์หากคุณมักไม่มีค่าซีเรียลไลซ์ที่ผิดพลาด


20
แต่จะเกิดอะไรขึ้นถ้าค่าที่ไม่ได้กำหนดค่าเป็นบูลีนที่มีค่า FALSE?
ปีเตอร์

1
@ ปีเตอร์: คำพูดที่ยอดเยี่ยม; ฉันแก้ไขคำตอบของฉันด้วยโจทย์เพื่อจัดการกับกรณีนั้น ขอบคุณ!
Pascal MARTIN

ขอบคุณ. :) ฉันคิดว่านี่อาจจะเป็นคำตอบ .. สำหรับฉันแล้วดูเหมือนว่าควรมีวิธีค้นหาว่ามันต่ออนุกรมกันหรือไม่ก่อนที่จะบังคับให้ parser พยายามประมวลผลจริง
แดง

1
วิธีนี้มีผลกระทบอย่างสมเหตุสมผลกับประสิทธิภาพของข้อมูลที่ใหญ่กว่าหรือไม่?
pie6k

2
ข้อสำคัญ: อย่ายกเลิกการเข้ารหัสข้อมูลผู้ใช้ดิบเนื่องจากสามารถใช้เป็นเวกเตอร์การโจมตีได้ OWASP: PHP_Object_Injection
ArtBIT

58

ฉันไม่ได้เขียนโค้ดนี้มาจาก WordPress จริงๆ คิดว่าจะรวมไว้เผื่อใครสนใจมันอาจจะมากเกินไป แต่ก็ใช้ได้ :)

<?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
โดยพื้นฐานแล้วฉันต้องการ regex เพื่อทำการตรวจจับพื้นฐานฉันลงเอยด้วยการใช้:^([adObis]:|N;)
farinspace

5
WordPress เวอร์ชันปัจจุบันค่อนข้างซับซ้อนกว่า: codex.wordpress.org/Function_Reference/…
ChrisV

3
+1 สำหรับการให้เครดิต ฉันไม่รู้ว่า WordPress มีสิ่งนี้ในตัว ขอบคุณสำหรับแนวคิด - ตอนนี้ฉันจะดำเนินการต่อและสร้างไฟล์เก็บถาวรของฟังก์ชันที่มีประโยชน์จาก WordPress Core
Amal Murali

URL ล่าสุดไปยังการอ้างอิงฟังก์ชัน wordpress: developer.wordpress.org/reference/functions/is_serialized
CédricFrançoys

20

เพิ่มประสิทธิภาพการตอบสนองของ Pascal MARTIN

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

18

ถ้าสตริง $เป็นfalseค่าซีเรียล$string = 'b:0;' ไลซ์ฟังก์ชันของSoN9neจะส่งกลับfalseมันผิด

ดังนั้นฟังก์ชันจะเป็น

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

2
การสลับลำดับของการทดสอบเหล่านี้จะมีประสิทธิภาพมากขึ้น
artfulrobot

ไม่ควรใช้เครื่องหมาย @ (ที่ตัวดำเนินการ) ใช้ try catch block แทน
Francisco Luz

@FranciscoLuz จากคู่มือphp.net/manual/en/function.unserialize.php In case the passed string is not unserializeable, FALSE is returned and E_NOTICE is issued. เราไม่สามารถตรวจจับข้อผิดพลาด E_NOTICE ได้เนื่องจากไม่ใช่ข้อยกเว้นที่ถูกโยนทิ้ง
Hazem Noor

@HazemNoor ฉันทดสอบด้วย PHP 7 แล้วมันก็ติด นอกจากนี้ใน PHP 7 ยังมี catch (\ Throwable $ e) ซึ่งจับทุกสิ่งที่ผิดพลาดภายใต้ประทุน
Francisco Luz

@FranciscoLuz คุณจับ E_Notice ใน PHP 7 ได้อย่างไร
user427969

13

แม้จะมีคำตอบที่ยอดเยี่ยมของ Pascal MARTIN แต่ฉันก็อยากรู้ว่าคุณสามารถเข้าถึงวิธีอื่นได้หรือไม่ดังนั้นฉันจึงทำสิ่งนี้เพื่อเป็นการฝึกจิต

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

และมันใช้งานได้จริง ข้อแม้เพียงอย่างเดียวคือว่ามันมีแนวโน้มที่จะทำลายถ้าคุณมีจัดการข้อผิดพลาดที่ลงทะเบียนเนื่องจากวิธีการ$ งาน


1
+1: อันนี้สนุกต้องยอมรับ - คงไม่ได้คิดอะไร! และฉันก็ไม่พบวิธีที่จะทำให้มันล้มเหลวด้วย ^^ ทำได้ดีมาก! และขอบคุณสำหรับความคิดเห็นในคำตอบของฉัน: ถ้าไม่มีฉันก็คงไม่ได้เห็นคำตอบนี้
Pascal MARTIN

$ a = 'bla'; $ b = 'b: 0;'; พยายามยกเลิกการเข้ารหัส $ a แล้ว $ b ด้วยสิ่งนี้ทั้งสองจะล้มเหลวในขณะที่ $ b ไม่ควร
bardiir

ไม่ใช่ว่ามีความล้มเหลวมาก่อน เนื่องจาก $ php_errormsg จะยังคงมีข้อผิดพลาดในการทำให้เป็นอนุกรมจากก่อนหน้านี้และเมื่อคุณ deserialize เท็จมันจะล้มเหลว
bardiir

ใช่ แต่เฉพาะในกรณีที่คุณไม่ได้ตรวจสอบข้อผิดพลาดระหว่าง deserializing $aและ deserializing $bซึ่งไม่ใช่การออกแบบแอปพลิเคชันที่ใช้งานได้จริง
Peter Bailey


3

สร้างในฟังก์ชัน

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

1
regex นี้เป็นอันตรายมันจะกลับมาเป็นบวกเมื่อa:(หรือb:ฯลฯ ) อยู่ที่ไหนสักแห่งภายใน $ value ไม่ใช่ในตอนเริ่มต้น และใน^ที่นี้ไม่ได้หมายถึงจุดเริ่มต้นของสตริง มันทำให้เข้าใจผิดโดยสิ้นเชิง
Denis Chmel

3

มีโซลูชัน WordPress: (รายละเอียดอยู่ที่นี่)

    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
นี่จะเป็นจริงสำหรับสตริง JSON จำนวนมากเช่นกันใช่ไหม ดังนั้นจึงไม่น่าเชื่อถือที่จะระบุว่าสตริงสามารถยกเลิก / ต่ออนุกรมได้หรือไม่
Gordon

อาจจะจริง แต่ถ้าทางเลือกนั้นเป็นแบบอนุกรมหรือเป็นเพียงข้อความธรรมดาเหมือนเดิมสำหรับฉันมันก็ใช้งานได้เหมือนมีเสน่ห์
Björn3

1
@ Björn3 "มันใช้ได้กับฉันในกรณีเฉพาะนี้" เป็นความคิดที่แย่มากที่จะมีเมื่อเขียนโค้ด มีนักพัฒนาจำนวนมากที่ขี้เกียจหรือไม่คิดไปข้างหน้าเช่นนี้และทำให้เกิดฝันร้ายในภายหลังเมื่อนักพัฒนารายอื่นต้องทำงานกับโค้ดของตนหรือพยายามเปลี่ยนแปลงบางอย่างและทันใดนั้นก็ไม่มีอะไรทำงานได้อย่างถูกต้องอีกต่อไป
BadHorsie

การสร้างโค้ดที่สมบูรณ์อย่างสมบูรณ์ (หากทำได้) ไม่ใช่เป้าหมายหรือแนวทางปฏิบัติที่ดีที่สุดเสมอไป ไม่ใช่เมื่อถึงเวลาหมดอายุ นี่เป็นความจริงจากมุมมองของโปรแกรมเมอร์เท่านั้น ในชีวิตจริงมีสารก่อโรคจำนวนมากซึ่งเป็นวิธีที่ต้องการอย่างรวดเร็วและสกปรก
Björn3

1

วิธีนี้ใช้ได้ดีสำหรับฉัน

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

?>

โปรดทราบว่าสิ่งนี้จะตรวจสอบว่าสตริงที่กำหนดเป็นสตริงที่ดูเป็นอนุกรมหรือไม่ซึ่งจะไม่ตรวจสอบความถูกต้องของสตริงนั้น
สร้าง

0

ดูฟังก์ชัน wordpress is_serialized

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;

}

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.