0

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

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

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

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

是否有更安全的序列化替代方案来替代没有 JSON 等性能开销的 pickle?

我们使用的是 Python 3.3。

4

1 回答 1

0

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

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

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

这意味着,与 JSON 可以轻松挂钩任何可以简化为字符串、浮点数、列表和字典的方式一样,BJSON 和 BSON 可以轻松挂钩任何可以简化为字符串、浮点数、列表、字典的内容,和字节串。因此,当我展示如何将 NumPy 编码/解码为字符串时,同样的事情适用于字节字符串,但最后没有所有额外的步骤。

BJSON 和 BSON 的缺点是它们不是人类可读的,并且没有那么广泛的支持。


我不知道您当前是如何对数组进行编码的,但是从时间上看,我怀疑您正在使用该tolist方法或类似的方法。那肯定会很慢,而且很大。如果您在任何地方存储除f8值以外的任何内容,它甚至会丢失信息(因为 JSON 理解的唯一数字是 IEEE 双精度数)。解决方案是编码为字符串。

NumPy 有一种文本格式,它会更快,而且不会有损,但仍然可能比你想要的更慢和更大。

它还具有二进制格式,这很棒……但没有足够的信息来恢复您的原始数组。

所以,让我们看看有什么pickle用途,你可以通过__reduce__在任何对象上调用方法来看到:基本上,它是类型、形状、dtype、一些告诉 NumPy 如何解释原始数据的标志,然后是二进制格式原始数据。您实际上可以__reduce__自己对数据进行编码——事实上,这样做可能是值得的。但是,为了便于说明,让我们做一些更简单的事情,并理解它只能在ndarray.

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'对象,Python 3json不知道如何处理。

如果您使用 BJSON 或 BSON 之类的东西,您可以在此处停止。但是对于 JSON,您需要字符串。

您可以通过使用映射每个单字节字符的任何编码(例如 Latin-1: change obj.tostring()toobj.tostring().decode('latin-1')data = obj['_npdata']to )“解码”字节来轻松解决这个问题,如果不巧的话data = obj['_npdata'].encode('latin-1')。通过 UTF-8 编码伪造的 Latin-1 字符串会浪费一些空间,但这还不错

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

曾经有一个技巧可以解决这个问题,但是在 3.3 中,它不起作用。您可以做的最好的事情是分叉或猴子补丁json包,这样它就可以让您在给定时传递控制字符ensure_ascii=False,您可以这样做:

json.encoder.ESCAPE = re.compile(r'"')

这很hacky,但它有效。


无论如何,希望这足以让你开始。

于 2013-11-11T08:01:42.493 回答