广告
您当前的位置: 首页 >  技术 >  Python

Python 深入浅出:数据序列化 json 与 pickle 的安全与选型

作者:CoderWang 时间:2026-06-29 阅读数:8人阅读

在进行网络通信、本地数据持久化或者分布式任务调度(如 Celery)时,我们必须要将内存中的 Python 对象转换为可以保存或网络传输的字节/文本格式。这个过程被称为序列化(Serialization)

Python 标准库提供了两个最常用的序列化模块: 1. json:跨语言的轻量级文本序列化协议。 2. pickle:Python 专用的强力二进制序列化协议。

然而,许多开发者在进行工具选型时,往往忽视了它们之间的安全性差异。盲目使用 pickle 来反序列化外部传来的数据,会导致极其致命的“远程代码执行(RCE)”漏洞。

本文将带您剖析这两大模块的底层差异、选型原则以及至关重要的安全红线。


一、 跨平台标准:json 模块

json 采用人类可读的纯文本格式,是当今互联网 API 交互的绝对标准。

核心特征:

  • 跨语言:支持所有的现代编程语言。
  • 数据限制:仅支持最基本的 Python 数据类型(dictliststrintfloatboolNone)。
  • 不支持自定义类:如果你尝试 json.dumps(User()) 序列化一个自定义类实例,会直接抛出 TypeError 异常(需要通过自定义 JSONEncoder 才能支持)。

二、 Python 独享二进制:pickle 模块

pickle(泡菜)是 Python 特有的序列化引擎。它几乎能够将任意复杂的 Python 对象(包括自定义类的实例、深层嵌套的树/图结构、甚至是函数和类定义本身)原封不动地打包成二进制字节流。

核心特征:

  • 仅限 Python:其他语言无法轻松解析 pickle 数据。
  • 支持任意对象:无需任何额外配置,直接支持自定义复杂类。

三、 致命死穴:pickle 反序列化远程代码执行(RCE)漏洞

pickle.loads(data) 隐藏着极具破坏性的安全漏洞。

漏洞机理分析:

在反序列化(重建对象)时,pickle 允许类通过重写内置方法 __reduce__ 来控制其重建行为。__reduce__ 可以返回一个可执行函数以及参数。当反序列化被触发时,Python 解释器会自动在后台调用执行这个函数

如果恶意黑客伪造了 pickle 字节流,并将 __reduce__ 关联为 os.system('rm -rf /') 或木马下载指令,只要你的代码调用了 pickle.loads(),服务器就会立刻中招被黑!

漏洞代码复现(警示演示):

import pickle
import os

class MaliciousPayload:
    def __reduce__(self):
        # 声明在反序列化时,自动调用系统命令打开计算器(或下载木马)
        return (os.system, ('calc.exe',))

# 1. 黑客在本地生成恶意字节流
payload_bytes = pickle.dumps(MaliciousPayload())

# 2. 你的服务器收到数据后,毫无防备地调用了 loads
print("⚠️ 正在反序列化未经验证的数据...")
pickle.loads(payload_bytes)  # 此时计算器直接被弹出,意味着系统已被攻破!

铁律:永远不要对任何来自客户端、Cookie、未经验证的网络请求或外部上传的 pickle 二进制数据调用 loads()


四、 优雅安全的自定义对象 json 序列化

如果确实需要传输或保存自定义类对象,推荐使用更安全的 json 并通过继承 json.JSONEncoder 来完成编码:

import json

class User:
    def __init__(self, name, age):
        self.name = name
        self.age = age

# 1. 自定义编码器
class UserEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, User):
            # 将自定义对象拆解为基础 dict 结构
            return {"__type__": "User", "name": obj.name, "age": obj.age}
        return super().default(obj)

# 2. 自定义解码器
def user_decoder(dct):
    if "__type__" in dct and dct["__type__"] == "User":
        return User(dct["name"], dct["age"])
    return dct

# ==================== 测试运行 ====================
user = User("Alice", 25)

# 序列化:传入自定义编码器类
json_str = json.dumps(user, cls=UserEncoder)
print("JSON 字符串:", json_str)

# 反序列化:传入自定义解码钩子函数
new_user = json.loads(json_str, object_hook=user_decoder)
print(f"反序列化成功: {new_user.name} | 年龄: {new_user.age}")

五、 总结与选型黄金法则

  1. 外部通信,只用 json:所有暴露给外部接口、Web API、客户端的数据交互,严禁使用 pickle,必须使用 jsonProtocol Buffers 等安全格式。
  2. 内部受信环境,才用 pickle:只有在 100% 受信的内部环境下(如你自己的分布式节点间通信、本地安全缓存),且序列化对象过于复杂时,才考虑使用 pickle
  3. 数据校验先行:反序列化前,始终进行严格的权限校验和签名校验(如使用 HMAC),确保数据中途未被黑客篡改。

筑牢序列化安全防线,是避免系统线上被攻破沦为肉鸡的关键基础!

本站所有文章、数据、图片均来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。

评论交流 (0)

正在加载评论...
头像

CoderWang

当你还撑不起你的梦想时,就要去奋斗。如果缘分安排我们相遇,请不要让她擦肩和过。我们一起奋斗!

微信