PHP序列化和反序列化语法差异问题
字数 921 2025-08-18 11:39:08

PHP序列化与反序列化语法差异深度解析

1. 基本概念

PHP序列化是将PHP值转换为包含字节流的字符串的过程,使用serialize()函数实现。反序列化则是将这种字符串重新转换为PHP原始值的过程,使用unserialize()函数实现。

关键特性:

  • 序列化会保存对象的所有变量,但不会保存对象的方法
  • 序列化只会保存类的名字
  • 反序列化时,对象的类必须已经定义过

2. 序列化内部机制

通过分析PHP内核源码(var.c),我们发现PHP序列化在默认情况下会在对象转换中加入:{}来拼接字符串。

核心代码逻辑:

// var.c Line:882
static void php_var_serialize_intern()
{
    // ...
    // Line:896
    if (ce->serialize(struc, &serialized_data, &serialized_length, (zend_serialize_data *)var_hash) == SUCCESS) {
        smart_str_appendl(buf, "C:", 2);
        smart_str_append_unsigned(buf, ZSTR_LEN(Z_OBJCE_P(struc)->name));
        smart_str_appendl(buf,2);
        smart_str_append(buf, Z_OBJCE_P(struc)->name);
        smart_str_appendl(buf,2);
        smart_str_append_unsigned(buf, serialized_length);
        smart_str_appendl(buf, ":{", 2);
        smart_str_appendl(buf, (char *) serialized_data, serialized_length);
        smart_str_appendc(buf,
    // Line:952
    smart_str_appendl(buf, ":{", 2);
    // Line:995
    smart_str_appendc(buf, '}');
}

关键点:

  • PHP使用smart_str_appendl为序列化字符串前后拼接:{}
  • 第896行进行序列化字符串拼接
  • 第952行和第995行对内嵌方法进行拼接

3. 反序列化内部机制

反序列化是将序列化的字符串按照特定语法规则进行转化还原的过程。

核心代码逻辑:

// var_unserialize.c Line:655
static int php_var_unserialize_internal()
{
    // ...
    // Line:674
    {
        YYCTYPE yych;
        static const unsigned char yybm[] = {
            // ... 省略字节映射表
        };
        
        if ((YYLIMIT - YYCURSOR) < 7) YYFILL(7);
        yych = *YYCURSOR;
        switch (yych) {
            case 'C': case 'O': goto yy4;
            case 'N': goto yy5;
            case 'R': goto yy6;
            case 'S': goto yy7;
            case 'a': goto yy8;
            case 'b': goto yy9;
            case 'd': goto yy10;
            case 'i': goto yy11;
            case 'o': goto yy12;
            case 'r': goto yy13;
            case 's': goto yy14;
            case '}': goto yy15;
            default: goto yy2;
        }
    }
    // Line:776
    yy15:
        ++YYCURSOR;
        {
            /* this is the case where we have less data than planned */
            php_error_docref(NULL, E_NOTICE, "Unexpected end of serialized data");
            return 0; /* not sure if it should be 0 or 1 here? */
        }
}

关键点:

  • 反序列化使用词法扫描器判断各项符号并转换为对应对象
  • 对于}的处理只是对计数器加一,没有其他操作
  • 这种处理方式导致了序列化和反序列化之间的语法差异

4. 安全影响

4.1 攻击面

这种语法差异对安全防护设备的反序列化检测产生了重大影响。例如在Snort规则中:

alert tcp any any -> any [80,8080,443] (uricontent:".php"; pcre:"/\{\w:sid:1; msg:php_serialize;)

攻击者可以利用大多数字符代替{},从而导致规则失效。

4.2 防御建议

对于蓝队防御,应考虑以下策略:

  1. 拦截保存的类名(根据定义中"不会保存对象的方法,只会保存类的名字")
  2. 检测语法中的特殊字符,如冒号
  3. 实现更严格的语法解析,不依赖特定分隔符

5. 实际应用

5.1 红队利用

攻击者可以利用PHP序列化和反序列化语法差异:

  • 使用替代字符绕过WAF规则
  • 构造特殊格式的序列化字符串绕过检测
  • 利用反序列化处理}的方式差异实现绕过

5.2 开发注意事项

开发者应注意:

  • 不要反序列化不可信的输入
  • 实现严格的输入验证
  • 考虑使用JSON等更安全的序列化格式替代PHP原生序列化

6. 环境配置

文章中使用的环境:

  • PHP 7.3.1
  • SDK VSCode
  • C++和C环境

配置建议参考:《WINDOWS下用VSCODE调试PHP7源代码》(原文链接已部分隐藏)

PHP序列化与反序列化语法差异深度解析 1. 基本概念 PHP序列化是将PHP值转换为包含字节流的字符串的过程,使用 serialize() 函数实现。反序列化则是将这种字符串重新转换为PHP原始值的过程,使用 unserialize() 函数实现。 关键特性: 序列化会保存对象的所有变量,但不会保存对象的方法 序列化只会保存类的名字 反序列化时,对象的类必须已经定义过 2. 序列化内部机制 通过分析PHP内核源码(var.c),我们发现PHP序列化在默认情况下会在对象转换中加入 :{ 和 } 来拼接字符串。 核心代码逻辑: 关键点: PHP使用 smart_str_appendl 为序列化字符串前后拼接 :{ 和 } 第896行进行序列化字符串拼接 第952行和第995行对内嵌方法进行拼接 3. 反序列化内部机制 反序列化是将序列化的字符串按照特定语法规则进行转化还原的过程。 核心代码逻辑: 关键点: 反序列化使用词法扫描器判断各项符号并转换为对应对象 对于 } 的处理只是对计数器加一,没有其他操作 这种处理方式导致了序列化和反序列化之间的语法差异 4. 安全影响 4.1 攻击面 这种语法差异对安全防护设备的反序列化检测产生了重大影响。例如在Snort规则中: 攻击者可以利用大多数字符代替 {} ,从而导致规则失效。 4.2 防御建议 对于蓝队防御,应考虑以下策略: 拦截保存的类名(根据定义中"不会保存对象的方法,只会保存类的名字") 检测语法中的特殊字符,如冒号 实现更严格的语法解析,不依赖特定分隔符 5. 实际应用 5.1 红队利用 攻击者可以利用PHP序列化和反序列化语法差异: 使用替代字符绕过WAF规则 构造特殊格式的序列化字符串绕过检测 利用反序列化处理 } 的方式差异实现绕过 5.2 开发注意事项 开发者应注意: 不要反序列化不可信的输入 实现严格的输入验证 考虑使用JSON等更安全的序列化格式替代PHP原生序列化 6. 环境配置 文章中使用的环境: PHP 7.3.1 SDK VSCode C++和C环境 配置建议参考:《WINDOWS下用VSCODE调试PHP7源代码》(原文链接已部分隐藏)