Compress complex data types into a string
Serialize () encodes variables and their values into text form
Unserialize () restore original variable
Eg:
The code is as follows: |
Copy code |
$ Stooges = array ('Moe', 'Larry ', 'Curly '); $ New = serialize ($ stooges ); Print_r ($ new ); Echo "<br/> "; Print_r (unserialize ($ new )); Result: A: 3: {I: 0; s: 3: "Moe"; I: 1; s: 5: "Larry"; I: 2; s: 5: "Curly ";} Array ([0] => Moe [1] => Larry [2] => Curly) |
When the serialized data is placed in a URL and transmitted between pages, you need to call urlencode () to ensure that the URL metacharacters in the URL are processed:
The code is as follows: |
Copy code |
$ Shopping = array ('Poppy seed bagel '=> 2, 'plain Bagel' => 1, 'lor' => 4 ); Echo '<a href = "next. php? Cart = '. urlencode (serialize ($ shopping).' "> next </a> '; |
The settings of the margic_quotes_gpc and magic_quotes_runtime parameters affect the data transmitted to unserialize.
If magic_quotes_gpc is enabled, stripslashes () must be used to process the data transmitted in URLs, POST variables, and cookies before Deserialization:
The code is as follows: |
Copy code |
$ New_cart = unserialize (stripslashes ($ cart )); // If magic_quotes_gpc is enabled $ New_cart = unserialize ($ cart ); |
If magic_quotes_runtime is enabled, you must use addslashes () for processing before writing serialized data to the file, and use stripslashes () for processing before reading them:
The code is as follows: |
Copy code |
$ Fp = fopen ('/tmp/cart', 'w '); Fputs ($ fp, addslashes (serialize ($ ))); Fclose ($ fp ); // If magic_quotes_runtime is enabled $ New_cat = unserialize (stripslashes (file_get_contents ('/tmp/cart '))); // If magic_quotes_runtime is disabled $ New_cat = unserialize (file_get_contents ('/tmp/cart ')); |
When magic_quotes_runtime is enabled, the serialized data read from the database must also be processed by stripslashes (). The serialized data saved to the database must be processed by addslashes, in order to be properly stored.
The code is as follows: |
Copy code |
Mysql_query ("insert into cart (id, data) values (1, '". addslashes (serialize ($ cart ))."')"); $ Rs = mysql_query ('select data from cart where id = 1 '); $ Ob = mysql_fetch_object ($ rs ); // If magic_quotes_runtime is enabled $ New_cart = unserialize (stripslashes ($ ob-> data )); // If magic_quotes_runtime is disabled $ New_cart = unserialize ($ ob-> data ); |
When an object is deserialized, PHP automatically calls its _ wakeUp () method. This allows the object to re-establish various states that are not retained during serialization. For example, database connection.
Let me explain it to you using examples.
The code is as follows: |
Copy code |
<? Php // Declare a class Class dog { Var $ name; Var $ age; Var $ owner; Function dog ($ in_name = "unnamed", $ in_age = "0", $ in_owner = "unknown "){ $ This-> name = $ in_name; $ This-> age = $ in_age; $ This-> owner = $ in_owner; } Function getage (){ Return ($ this-> age * 365 ); } Function getowner (){ Return ($ this-> owner ); } Function getname (){ Return ($ this-> name ); } } // Instantiate this class $ Ourfirstdog = new dog ("Rover", 12, "Lisa and Graham "); // Use the serialize function to convert this instance into a serialized string $ Dogdisc = serialize ($ ourfirstdog ); Print $ dogdisc; // $ ourfirstdog has been serialized as a string O: 3: "dog": 3: {s: 4: "name"; s: 5: "Rover "; s: 3: "age"; I: 12; s: 5: "owner"; s: 15: "Lisa and Graham ";} /* Bytes ----------------------------------------------------------------------------------------- Here you can store the string $ dogdisc anywhere, such as session, cookie, database, and PHP files. Bytes ----------------------------------------------------------------------------------------- */ // Cancel this class here Unset ($ ourfirstdog ); ?> B. php <? Php ?> <? Php // Declare a class Class dog { Var $ name; Var $ age; Var $ owner; Function dog ($ in_name = "unnamed", $ in_age = "0", $ in_owner = "unknown "){ $ This-> name = $ in_name; $ This-> age = $ in_age; $ This-> owner = $ in_owner; } Function getage (){ Return ($ this-> age * 365 ); } Function getowner (){ Return ($ this-> owner ); } Function getname (){ Return ($ this-> name ); } } /* Restore operation */ /* Bytes ----------------------------------------------------------------------------------------- Here, you can read the string $ dogdisc from your storage location, such as session, cookie, database, and PHP files. Bytes ----------------------------------------------------------------------------------------- */ $ Dogdisc = 'O: 3: "dog": 3: {s: 4: "name"; s: 5: "Rover"; s: 3: "age "; i: 12; s: 5: "owner"; s: 15: "Lisa and Graham ";}'; // Here we use unserialize () to restore serialized objects. $ Pet = unserialize ($ dogdisc); // $ pet is the previous $ ourfirstdog object. // Get the age and name attributes $ Old = $ pet-> getage (); $ Name = $ pet-> getname (); // This class can be used without instantiation at this time, and both attributes and values remain in the state before serialization. Print "Our first dog is called $ name and is $ old days old <br> "; ?> |
Security risks caused by inconsistent serialization and deserialization syntax parsing
. PHP string serialize ()-related source code analysis
------------------------------------
The code is as follows: |
Copy code |
Static inline void php_var_serialize_string (smart_str * buf, char * str, int len )/*{{{*/ { Smart_str_appendl (buf, "s:", 2 ); Smart_str_append_long (buf, len ); Smart_str_appendl (buf, ": \" ", 2 ); Smart_str_appendl (buf, str, len ); Smart_str_appendl (buf, "\"; ", 2 ); } |
The code snippet above shows how serialize () serializes strings as follows:
The code is as follows: |
Copy code |
$ Str = 'ryatsyne '; Var_dump (serialize ($ str )); // $ Str serialized string output // S: 8: "ryatsyne "; |
Ii. PHP string unserialize () source code analysis
---------------------------------------
The unserialize () function deserializes strings in two formats:
The code is as follows: |
Copy code |
Switch (yych ){ ... Case's ': goto yy9; ... Yy9: Yych = * (YYMARKER = ++ YYCURSOR ); If (yych = ':') goto yy46; Goto yy3; ... Yy46: Yych = * ++ YYCURSOR; If (yych = '+') goto yy47; If (yych <= '/') goto yy18; If (yych <= '9') goto yy48; Goto yy18; Yy47: Yych = * ++ YYCURSOR; If (yych <= '/') goto yy18; If (yych> = ':') goto yy18; Yy48: ++ YYCURSOR; If (YYLIMIT-YYCURSOR) <2) YYFILL (2 ); Yych = * YYCURSOR; If (yych <= '/') goto yy18; If (yych <= '9') goto yy48; If (yych> = ';') goto yy18; Yych = * ++ YYCURSOR; If (yych! = '"') Goto yy18; ++ YYCURSOR; { Size_t len, maxlen; Char * str; Len = parse_uiv (start + 2 ); Maxlen = max-YYCURSOR; If (maxlen <len ){ * P = start + 2; Return 0; } Str = (char *) YYCURSOR; YYCURSOR + = len; If (* (YYCURSOR )! = '"'){ * P = YYCURSOR; Return 0; } // Make sure the format is s: x: "x" YYCURSOR + = 2; * P = YYCURSOR; // Note here, * the p pointer directly moves two digits behind, that is to say, it does not judge whether "is followed; INIT_PZVAL (* rval ); ZVAL_STRINGL (* rval, str, len, 1 ); Return 1; |
The other is to process the series string in the S: format (this format is not defined in serialize () function serialization ):
The code is as follows: |
Copy code |
Static char * unserialize_str (const unsigned char ** p, size_t * len, size_t maxlen) { Size_t I, j; Char * str = safe_emalloc (* len, 1, 1 ); Unsigned char * end = * (unsigned char **) p + maxlen; If (end <* p ){ Efree (str ); Return NULL; } For (I = 0; I <* len; I ++ ){ If (* p> = end ){ Efree (str ); Return NULL; } If (** p! = '\\'){ Str [I] = (char) ** p; } Else { Unsigned char ch = 0; For (j = 0; j <2; j ++ ){ (* P) ++; If (** p> = '0' & ** p <= '9 '){ Ch = (ch <4) + (** p-'0 '); } Else if (** p> = 'A' & ** p <= 'F '){ Ch = (ch <4) + (** p-'A' + 10 ); } Else if (** p> = 'A' & ** p <= 'F '){ Ch = (ch <4) + (** p-'A' + 10 ); } Else { Efree (str ); Return NULL; } } Str [I] = (char) ch; } (* P) ++; } Str [I] = 0; * Len = I; Return str; } // The above function is to convert a hexadecimal string like \ 72 \ 79 \ 61 \ 74 \ 73 \ 79 \ 6e \ 65 ... Switch (yych ){ ... Case's ': goto yy10; // The processing process is the same as that of s: If (str = unserialize_str (& YYCURSOR, & len, maxlen) = NULL ){ Return 0; } // The processing process is the same as that of s: |
From the code snippet above, we can see that unserialize () processes the serialized string deserialization as follows:
The code is as follows: |
Copy code |
$ Str1 ='s: 8: "ryatsyne ";'; $ Str2 ='s: 8: "ryatsyne" t '; $ Str3 ='s: 8: "\ 72 \ 79 \ 61 \ 74 \ 73 \ 79 \ 6e \ 65 "'; Var_dump (unserialize ($ str )); // $ Str1, $ str2 and $ str3 unserialized string output // Ryatsyne; |
Iii. Security risks caused by inconsistent syntax parsing
-----------------------------
From the above analysis process, we can see that PHP does not strictly follow the serialization format s: x: "x"; when deserializing the string, and does not judge whether "is followed, at the same time, the processing of hexadecimal strings is added, which makes the inconsistency between the pre-processing and post-processing confusing. Due to the absence of detailed descriptions in the PHP Manual, most programmers do not understand this process, which may lead to omissions in the coding process, and even cause serious security problems.
Back to the IPB vulnerability mentioned at the beginning of the article, we can use this funny feature of PHP to easily filter bypass safeUnserialize () function :)
The code is as follows: |
Copy code |
* Mixed safe_unserialize (string $ serialized) * Safely unserialize, that is only unserialize string, numbers and arrays, not objects * * @ License Public Domain * @ Author dcz (at) phpbb-seo (dot) com */ Static public function safeUnserialize ($ serialized) { // Unserialize will return false for object declared with small cap o // As well as if there is any ws between O and: If (is_string ($ serialized) & strpos ($ serialized, "\ 0") = false) { If (strpos ($ serialized, 'O: ') = false) { // The easy case, nothing to worry about // Let unserialize do the job Return @ unserialize ($ serialized ); } Else if (! Preg_match ('/(^ |; | {|}) O: [+ \-0-9] +: "/', $ serialized )) { // In case we did have a string with O: in it, // But it was not a true serialized object Return @ unserialize ($ serialized ); } } Return false; } // A: 1: {s: 8: "ryatsyne" tO: 8: "ryatsyne": 0 :{}} // As long as similar serialized strings are constructed, the filter here can be easily broken through. |