如果我自己在 Python 中 pickle 一个字符串,unpickling 它会不会很危险?

If I pickle a string in Python myself, can unpickling it ever be dangerous?

假设我们有一个 Postgres 12 数据库,它有一个名为 MyClass 的 table,它有一个名为 notes 的文本列。用户可以在此注释字段中保存他们想要的任何内容。出于这个问题的目的,我们假设他们以某种方式绕过了所有数据清理。

以下代码行是否会因 obj.notes 中的恶意文本而变得危险?

import pickle
# (obj is a Python3 instance of MyClass using the Django ORM, so obj.notes is always represented as a unicode string)
obj = MyClass.objects.get(id=1)
pickled = pickle.dumps(obj.notes)
unpickled = pickle.loads(pickled)

使用您无法控制的其他来源的 pickle 文件是极其危险的,因为 pickle 可能包含代码。该代码几乎可以是任何内容,包括针对您的系统的 shell 命令。

酸洗也并不总是像您描述的那样安全 - 采用 ORM 类,酸洗它们,然后取消酸洗它们可能会导致新的 类 没有正确链接到数据库会话。

在您的示例中,我将保存 ID 并使用它从数据库中重新加载对象。对于我想将数据移入和移出应用程序的其他事情,我会推荐 pyyaml 中的 load_safe 函数或 json 中的 loads(使用默认编码器)。

python pickle 协议(版本 4)将字符串序列化为:一个标记,后跟字符串的长度,再后跟 utf-8 编码的内容。令牌是一个代码,用于标记数据将被解释为字符串(并指定中间整数的数据大小)。因此理论上,所有编码的字符串数据都将直接复制到新的字符串对象中而不进行解析(字符串的内容没有机会影响 unpickler 机器的行为)。

这意味着即使是恶意字符串也应该在不做任何更改的情况下进行 pickle 和 unpickle,并且没有机会劫持 unpickler 机器和 运行 任意代码(不像 pickled 数据本身已经被破坏)。

import pickle, pickletools
pickletools.dis(pickle.dumps("Hello World"))

详情见pickletools comments

早些时候(协议版本 0)协议没有指定固定长度,而是使用定界符来终止字符串,并期望应用转义(以防同样的定界符也出现在字符串中)。或者,即使使用现有协议,您也可以重新实现 pickler 以执行重复序列的字符串压缩。无论哪种方式,安全性都取决于您的 pickle 库的实现是否没有错误。