如何安全地從操作系統級別的Python沙箱中返回物件?

2024-05-17 11:58:29 发布

您现在位置:Python中文网/ 问答频道 /正文

我需要能够运行不受信任的Python脚本。经过大量研究,似乎只有Python的沙盒是不安全的,至少对于CPython(我需要使用CPython)。在

因此,我们也计划使用操作系统级别的沙盒(SELinux、AppArmor等)。在

我的问题是:我们如何安全地与沙盒通信?沙盒中的代码需要返回Python类型,如int和str,以及Numpy数组。未来可能会有更多的类型。在

显而易见的方法是使用pickle,但沙盒中的一些恶意代码可能会获得输出管道(我们正在考虑使用0MQ),并将可能导致在沙盒外部取消拾取时执行任意代码的内容发回。在

JSON和pickle的串行化开销有没有比pickle更安全的性能呢?在

我们使用的是python3.3。在


Tags: 代码numpy脚本沙盒类型数组cpython级别
1条回答
网友
1楼 · 发布于 2024-05-17 11:58:29

听起来JSON的唯一真正问题是编码NumPy数组(和Pandas表)的方式。JSON对于您的用例来说并不理想,不是因为它处理NumPy数据的速度很慢,而是因为它是基于文本的格式,而且您有很多数据更容易以非文本格式进行编码。在

因此,我将在下面向您展示一种解决JSON问题的方法……但我建议使用不同的格式。在

两种主要的“二进制JSON”格式BJSONBSON,旨在提供JSON的大部分优点(简单、安全、动态/无模式、可遍历等),同时也使直接嵌入二进制数据成为可能。(在本例中,它们也是二进制格式而不是文本格式这一事实对您来说并不重要。)我相信Smile也是如此,但我从未使用过它。在

这意味着,与JSON一样,JSON可以轻松地钩住任何可以简化为string、float、list和dict的内容,BJSON和BSON可以轻松地钩住任何可以简化为string、floats、list、dicts、和byte strings的内容。所以,当我演示如何将NumPy编码/解码为字符串时,同样的方法也适用于字节字符串,但在结尾没有所有额外的步骤。在

BJSON和BSON的缺点是它们不适合人类阅读,也没有得到广泛的支持。在


我不知道您当前是如何编码数组的,但从计时来看,我怀疑您在使用tolist方法或类似方法。这肯定是缓慢的,而且是巨大的。如果您在任何地方存储f8以外的任何值,它甚至会丢失信息(因为JSON只理解IEEE的双精度数)。解决方案是编码成一个字符串。在

NumPy有一个文本格式,它会更快,不会有损失,但可能仍然会比您想要的更慢和更大。在

它还有一个二进制格式,这很好…但是没有足够的信息来恢复原始数组。在

那么,让我们看看pickle使用了什么,你可以通过对任何对象调用__reduce__方法来看到:基本上,是类型、形状、数据类型、一些标志告诉NumPy如何解释原始数据,然后是二进制格式的原始数据。实际上,您可以自己对__reduce__数据进行编码实际上,这样做是值得的。但是为了便于说明,让我们做一些更简单的事情,理解它只适用于ndarray,而不适用于具有不同端点的机器(或者更罕见的情况,如符号大小整数或非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()改为{},将data = obj['_npdata']改为{}。用UTF-8编码伪拉丁1字符串浪费了一点空间,但这也不算太糟。在

不幸的是,Python将用Unicode转义序列对每个非ASCII字符进行编码。您可以通过在转储上设置ensure_ascii=False和在加载上设置strict=False来关闭它,但它仍然会对控制字符进行编码,大部分是6字节序列。这会使随机数据的大小翻倍,而且会造成更糟的结果——例如,全零数组将大6倍!在

以前有一个技巧可以解决这个问题,但是在3.3中,它不起作用。您可以做的最好的事情是对json包进行fork或monkey补丁,以便在给定ensure_ascii=False时传递控制字符,您可以这样做:

^{pr2}$

这很老套,但很管用。在


不管怎样,希望这足以让你开始。在

相关问题 更多 >