我需要能够运行不受信任的Python脚本。经过大量的研究,似乎只有Python的沙箱是不安全的,至少使用CPython (我需要使用)。
因此,我们也计划使用OS级别的沙箱(SELinux、AppArmor等)。
我的问题是:我们如何安全地与沙箱通信?沙箱中的代码需要返回Python类型,如int和str,以及Numpy数组。将来可能会有更多的类型。
显而易见的方法是使用泡菜,但沙箱中的一些恶意代码似乎可以获得输出管道(我们正在考虑使用0MQ),并发回一些可能导致在沙箱之外不加腌制时执行任意代码的内容。
是否有不具备JSON等性能开销的更安全的泡菜序列化替代方案?
我们正在使用Python3.3。
发布于 2013-11-11 08:01:42
听起来,JSON唯一真正的问题是编码NumPy数组(和Pandas表)的方式。对于用例来说,JSON并不理想--不是因为它处理NumPy数据的速度慢,而是因为它是一种基于文本的格式,而且您有很多数据更容易在非基于文本的格式中编码。
因此,我将向您展示如何解决…中JSON的所有问题。但我建议采用另一种格式。
两种主要的“二进制JSON”格式,BJSON和布森,旨在提供JSON的大部分优点(简单、安全、动态/无模式、可遍历等),同时也使直接嵌入二进制数据成为可能。(在本例中,它们也是二进制格式而不是文本格式,这对您来说并不重要。)我相信微笑也是如此,但我从未使用过它。
这意味着,以同样的方式,JSON使您可以轻松地将任何可以还原为字符串、浮点、列表和dicts的东西挂上钩,BJSON和BSON使您可以轻松地连接到任何可以还原为字符串、浮点数、列表、块和字节字符串的东西。因此,当我演示如何将NumPy编码/解码为字符串时,同样的方法也适用于字节字符串,但在结束时不需要所有额外的步骤。
BJSON和BSON的缺点是它们不是人类可读的,而且没有得到广泛的支持。
我不知道您目前是如何编码数组的,但从时间上看,我怀疑您正在使用tolist方法或类似的方法。那肯定会很慢,而且很大。如果您在任何地方存储f8值以外的任何东西,它甚至会丢失信息(因为JSON所能理解的唯一类型的数字是IEEE )。解决方案是将代码编码为字符串。
NumPy有一种文本格式,它将是更快的,而不是有损的,但可能仍然比您想要的更慢和更大。
它还有一个二进制格式,这是一个很好的…。但是没有足够的信息来恢复原来的数组。
那么,让我们看看pickle使用了什么,您可以通过在任何对象上调用__reduce__方法来看到:基本上,它是类型、形状、dtype、一些标志告诉NumPy如何解释原始数据,然后是二进制格式的原始数据。实际上,您可以自己对__reduce__数据进行编码--实际上,这样做可能是值得的。但是,为了说明起见,让我们做一些简单一些的事情吧,因为我们知道它只在ndarray上工作,不会在不同的机器上工作(或者更少见的情况,比如符号大小的ints或非IEEE浮动)。
def numpy_default(obj):
if isinstance(obj, np.ndarray):
return {'_npdata': obj.tostring(),
'_npdtype': obj.dtype.name,
'_npshape': obj.shape}
else:
return json.dumps(obj)
def dumps(obj):
return json.dumps(obj, default=numpy_default)
def numpy_hook(obj):
try:
data = obj['_npdata']
except AttributeError:
return obj
return np.fromstring(data, obj['_npdtype']).reshape(obj['_npshape'])
def loads(obj):
return json.loads(obj, object_hook=numpy_hook)唯一的问题是np.tostring为您提供了'bytes'对象,Python3的json不知道如何处理这些对象。
如果使用的是BJSON或BSON之类的东西,就可以在这里停止使用。但是使用JSON,您需要字符串。
如果麻烦的话,您可以通过使用映射每个单个字节字符的任何编码来“解码”字节,比如拉丁文-1:将obj.tostring()更改为obj.tostring().decode('latin-1'),将data = obj['_npdata']更改为data = obj['_npdata'].encode('latin-1')。这浪费了UTF-8编码假拉丁-1字符串的空间,但这还不算太糟。
不幸的是,Python会用Unicode转义序列对每个非ASCII字符进行编码。您可以通过在转储上设置ensure_ascii=False和在加载时设置strict=False来关闭它,但是它仍然会对控制字符进行编码,大部分编码为6字节序列。这使随机数据的大小翻了一番,而且它可以做得更糟--例如,一个全零数组将大6倍!
过去有一个解决这个问题的诡计,但在3.3中,它不起作用。您能做的最好的事情是对json包进行分叉或猴子补丁,这样当给定ensure_ascii=False时,它允许您传递控制字符,您可以这样做:
json.encoder.ESCAPE = re.compile(r'"')这很烦人,但很管用。
不管怎样,希望这足以让你开始工作。
https://stackoverflow.com/questions/19898981
复制相似问题