pickle module documentation在开头说:
警告: 泡菜模块不是为了防止错误或安全 恶意构造的数据。切勿取消从中收到的数据 不受信任或未经认证的来源。
然而,在restricting globals下面,它似乎描述了一种使用允许对象的白名单来确保使用unickling数据的方法。
这是否意味着如果我使用仅允许某些"基本"的RestrictedUnpickler
,我可以安全地解开不受信任的数据。类型,或者是否存在此方法未解决的其他安全问题?如果有,还有另一种方法可以使unpickling安全(显然代价是无法解开每一个流)?
使用"基本类型"我的意思是:
bool
str
,bytes
,bytearray
int
,float
,complex
tuple
,list
,dict
,set
和frozenset
答案 0 :(得分:5)
我甚至说没有安全的方法来使用pickle来处理不受信任的数据。
即使使用受限制的全局变量,Python的动态特性使得一个坚定的黑客仍然有机会找到回到__builtins__
映射并从那里到皇冠宝石的方法。
请Ned Batchelder's blog posts on circumventing restrictions on eval()
同等适用于pickle
。
请记住,pickle
仍然是一种堆栈语言,你无法预见所有可能的对象,即使是一组有限的全局变量也允许任意调用。 pickle文档也没有提到允许调用EXT*
的{{1}}操作码 - 已安装的扩展名;你必须在这里考虑安装在该注册表中的任何东西。所需要的只是一个向量,允许将对象调用转换为copyreg
等效物,以防止你的防御崩溃。
至少使用数据加密签名,以便验证完整性。你将限制风险,但如果攻击者曾设法窃取你的签名秘密(密钥),那么他们可能会再次打败你的黑客泡菜。
我会使用像JSON这样的现有无害格式并添加类型注释;例如使用类型键将数据存储在字典中,并在加载数据时进行转换。
答案 1 :(得分:2)
在解决在标准库中添加安全pickle
替代方案的问题时,也已在邮件列表python-ideas上讨论了这个想法。例如here:
为了让它更安全,我会将限制的unpickler作为默认值(对于加载/加载)并强制人们在想要放松限制时覆盖它。要真正明确,我会使加载/加载仅适用于内置类型。
还有here:
我一直想要一个pickle.loads()版本,它会获取允许实例化的类列表。
以下内容是否适合您:http://docs.python.org/3.4/library/pickle.html#restricting-globals?
确实如此。谢谢你指出来!我从来没有超过文档的模块界面部分。也许页面顶部的警告也可以提到有减轻安全问题的方法,并指出#restrictionting-globals?
是的,这是一个好主意: - )
所以我不知道为什么文档没有被更改但是根据我的说法,使用RestrictedUnpickler
来限制可以被打开的类型是一种安全的解决方案。当然,库中可能存在危害系统的错误,但OpenSSL中也可能存在一个错误,向所有要求的人显示随机内存数据。
答案 2 :(得分:2)
在这个答案中,我们将探究泡菜协议允许攻击者执行的操作。这意味着我们将仅依赖于协议的文档功能,而不依赖实现细节(有一些例外)。换句话说,我们假设pickle
模块的源代码是正确的且没有错误,并且可以让我们完全按照文档中的说明进行操作。
腌制allows classes to customize how their instances are pickled。在去皮过程中,我们可以:
__setstate__
方法(只要我们设法释放该类的实例)。__reduce__
方法(只要我们能够以某种方式获得对可调用对象的访问权),即可使用任意参数调用任意可调用对象。__reduce__
来调用(几乎)任何未腌制对象的append
,extend
和__setitem__
方法。Unpickler.find_class
允许我们使用的任何属性。str
,bytes
,list
,tuple
,dict
,int
,{{1 }},float
。这没有记录,但是这些类型内置在协议本身中,不会通过bool
。这里最有用的功能(从攻击者的角度来看)是能够调用可调用对象的功能。如果他们可以访问Unpickler.find_class
或exec
,则可以使我们执行任意代码。如果他们可以访问eval
或os.system
,则可以运行任意的shell命令。当然,我们可以拒绝他们使用subprocess.Popen
访问它们。但是,我们究竟应该如何实现我们的Unpickler.find_class
方法呢?哪些函数和类是安全的,哪些是危险的?
在这里,我将尝试解释攻击者可以用来做恶事的一些方法。使攻击者可以使用任何这些功能/类,意味着您有危险。
exec
和eval
(杜)os.system
,os.popen
,subprocess.Popen
和所有其他find_class
函数types.FunctionType
,它允许从代码对象创建函数(可以使用compile
或types.CodeType
创建)typing.get_type_hints
。是的,你看的没错。你怎么问?好吧,subprocess
会评估前向引用。因此,您所需要的只是一个typing.get_type_hints
对象,例如__annotations__
,而{'x': 'os.system("rm -rf /")'}
将为您运行代码。functools.singledispatch
。我看到你难以置信地摇了摇头,但这是真的。单调度功能具有get_type_hints
方法,该方法在内部调用register
。 无需通过typing.get_type_hints
即可访问内容:
仅仅因为我们的Unpickler.find_class
方法阻止了攻击者直接访问 并不意味着没有间接方式访问该东西。
find_class
访问对象的类,可以通过obj.__class__
访问类的父类,依此类推。
索引::很多东西存储在列表,元组和字典中-能够索引数据结构为攻击者打开了许多大门。
cls.__bases__
,list.__getitem__
等
请参阅Ned Batchelder的Eval is really dangerous,以了解攻击者如何使用它们来获取几乎所有内容的访问权限。
解开代码后的代码执行 :
攻击者不一定要在拔除过程中 做一些危险的事情-他们还可以尝试返回危险的对象并让 you 调用危险的函数事故。也许您在未腌制的物体上呼叫dict.__getitem__
,或者您可能想解开typing.get_type_hints
,但想解开CuteBunny
并在尝试FerociousDragon
时将其咬断。始终确保未腌制的对象属于您期望的类型,其属性属于您期望的类型,并且不具有您不期望的任何属性。
在这一点上,显然可以信任的模块/类/功能并不多。当您实现.pet()
方法时,永远不要永远写黑名单-总是写白名单,并且只包含您确定不会被滥用的内容。
如果您真的仅允许访问find_class
,bool
,str
,bytes
,bytearray
,{{1 }},int
,float
,complex
,tuple
,list
和dict
,那么您很有可能安全。但说实话-您可能应该改用JSON。
通常来说,我认为大多数类是安全的-当然,除了set
这样的例外。攻击者可以做的最坏的事情就是调用类-通常,该类不应做比返回该类的实例更危险的事情。
您真正需要注意的是允许访问功能(以及其他非类可调用对象)以及如何处理未腌制的对象。