Python:TypeError:出于安全原因,不允许选择AuthenticationString对象

时间:2015-03-12 10:45:31

标签: python class multiprocessing python-multiprocessing

我正在创建一个类的对象(带multiprocessing)并将其添加到Manager.dict(),以便我可以从对象内的字典中删除该项(该项指向)当它的工作完成时......

我尝试了以下代码:

from multiprocessing import Manager, Process

class My_class(Process):
    def __init__(self):
        super(My_class, self).__init__()
        print "Object", self, "created."

    def run(self):
        print "Object", self, "process started."


manager=Manager()
object_dict=manager.dict()

for x in range(2):
    object_dict[x]=My_class()
    object_dict[x].start()

但我收到了一个错误:

TypeError: Pickling an AuthenticationString object is disallowed
for security reasons

为了好奇,我删除了多处理部分,并尝试了:

from multiprocessing import Manager
class My_class():
    def __init__(self):
        print "Object", self, "created."

manager=Manager()
object_dict=manager.dict()

for x in range(2):
    object_dict[x]=My_class()

并且它没有给我任何错误并显示两个对象的地址。

该错误是什么以及如何使其消失?

2 个答案:

答案 0 :(得分:5)

这是复制您所看到的效果的较短方法:

from multiprocessing import Process
import pickle

p = Process()
pickle.dumps(p._config['authkey'])
  

TypeError:不允许腌制AuthenticationString对象   出于安全原因

这里实际发生的事情如下:process._config['authkey']Process对象在创建时被分配的secret key。尽管此键仅是字节序列,但Python使用bytes的特殊子类来表示它:AuthenticationString。该子类仅在一个方面与通常的bytes不同-它拒绝被腌制。

此选择背后的原理如下:authkey用于验证父级和子级进程之间(例如,工作进程与主进程之间)的进程间通信消息,并将其暴露在初始进程族之外的任何地方都可能构成安全风险(因为从原则上讲,您可以为工作人员模拟“父进程”,并迫使其执行任意代码)。由于酸洗是Python中最常见的数据传输形式,因此禁止酸洗是一种意外暴露authkey的简单方法。

由于您不能腌制AuthenticationString,所以也不能腌制Process类或其任何子类的实例(因为它们都在字段中包含身份验证密钥)。

现在让我们看看它们与您的代码之间的关系。您创建一个Manager对象,并尝试设置其dict的值。 Manager实际上是在一个单独的进程中运行,每当您向manager.dict()分配任何数据时,Python都需要将此数据传输到Manager's自己的进程中。为了进行这种传输,需要对数据进行腌制。但是,从前几段中我们知道,您不能腌制Process对象,因此根本无法将它们保留在共享的dict中。

简而言之,您可以自由使用manager.dict()共享任何对象,除了那些不能腌制的对象,例如Process对象。

答案 1 :(得分:0)

注意:下面的解决方案是在 Python3 aka print() 中。 Python3 中也存在同样的问题。

好吧,在您的具体示例中,我们可以通过将 AuthenticationString dict 中的 _config 酸洗作为字节缓冲区作为 Process 对象的一部分,然后优雅地恢复来解决该问题它在解酸时就好像什么也没发生一样。在 My_class 中定义为酸洗操作调用的 get 和 set 状态方法,如下所示:

from multiprocessing import Manager, Process
from multiprocessing.process import AuthenticationString

class My_class(Process):
    def __init__(self):
        super(My_class, self).__init__()
        print("Object", self, "created.")

    def run(self):
        print("Object", self, "process started.")

    def __getstate__(self):
        """called when pickling - this hack allows subprocesses to 
           be spawned without the AuthenticationString raising an error"""
        state = self.__dict__.copy()
        conf = state['_config']
        if 'authkey' in conf: 
            #del conf['authkey']
            conf['authkey'] = bytes(conf['authkey'])
        return state

    def __setstate__(self, state):
        """for unpickling"""
        state['_config']['authkey'] = AuthenticationString(state['_config']['authkey'])
        self.__dict__.update(state)

if __name__ == '__main__': # had to add this
    manager=Manager()
    object_dict=manager.dict()
    for x in range(2):
        object_dict[x]=My_class()
        object_dict[x].start()

我通过运行代码得到以下输出:

Object <My_class(My_class-2, initial)> created.
Object <My_class(My_class-3, initial)> created.
Object <My_class(My_class-2, started)> process started.
Object <My_class(My_class-3, started)> process started.

这似乎是预期的结果,如果您输入 time.sleep() 调用以保持它们的存活时间更长一些,您可以看到两个子进程正在运行。

或者,如果您只是删除该 _config authkey,然后您甚至不需要定义自定义 __setstate__ 方法,它似乎也不会造成任何问题。

另外,请注意,我必须添加 __main__ - 如果没有它,python 会抱怨在启动子进程之前没有完成引导。

最后,我只需要对这整个“安全”事情耸耸肩。它会在各种地方弹出(需要相同类型的变通方法)并且不提供任何真正的安全性。