【问题标题】:If I pickle a string in Python myself, can unpickling it ever be dangerous?如果我自己在 Python 中腌制一个字符串,解封它会很危险吗?
【发布时间】:2020-02-13 23:50:39
【问题描述】:

假设我们有一个 Postgres 12 数据库,它有一个名为 MyClass 的表,其中有一个名为 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)

【问题讨论】:

    标签: python django python-3.x security pickle


    【解决方案1】:

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

    这意味着即使是恶意字符串也应该在没有任何变化的情况下进行腌制和解封,并且没有机会劫持解封机并运行任意代码(与腌制数据本身已被泄露不同)。

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

    详情见pickletools comments

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

    【讨论】:

      【解决方案2】:

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

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

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

      【讨论】:

      • OP 控制源并保证它始终是一个字符串。您的回答是笼统的,但没有针对具体情况。话虽如此,你建议要么腌制 ID,要么使用更安全的方法。
      • 他们明确表示他们希望我们假设他们的用户已经绕过数据清理,所以我回答了当时无法控制数据的具体情况,因为这是他们要求的。
      • 但它仍然是一个字符串。是否有一个字符串会腌制到不会解开相同字符串的东西?
      • @tedivm:不过,我们不会在用户输入时调用loads。我们在序列化的结果用户输入上调用它。用户不能直接向loads 提供任意参数。
      • 攻击者绕过了obj.notes字符串的所有内容;他们没有完全控制pickledpickled 仍然来自 pickle.dumps(obj.notes),即使 obj.notes 是恶意的,也可以安全地解压。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-18
      • 1970-01-01
      • 2011-07-30
      • 2016-08-08
      • 2021-05-11
      • 2011-03-19
      相关资源
      最近更新 更多