从不受信任的来源安全地序列化数据和解包

时间:2012-10-07 16:47:41

标签: python yaml urllib2 pyramid pickle

我使用Pyramid作为基于回合制视频游戏的数据传输的基础。客户端使用POST数据来显示他们的操作,并使用GET来检索序列化的游戏板数据。游戏数据有时可能涉及字符串,但几乎总是两个整数和两个元组:

gamedata = (userid, gamenumber, (sourcex, sourcey), (destx, desty))

我的一般客户端框架是Pickle,转换为base 64,使用urlencode,然后提交POST。然后,服务器接收POST,解压缩单项字典,解码base64,然后取消对数据对象的去除。

我想使用Pickle,因为我可以使用类和值。提交游戏数据作为POST字段只能给我字符串。

然而,Pickle被认为是不安全的。所以,我转向pyYAML,它的用途相同。使用yaml.safe_load(data),我可以序列化数据而不会暴露安全漏洞。但是,safe_load是非常安全的,我甚至无法反序列化无害的元组或列表,即使它们只包含整数。

这里有一些中间地带吗?有没有办法在不同时允许执行任意代码的情况下序列化python结构?

我的第一个想法是为我的发送和接收函数编写一个包装器,它在值名称中使用下划线来重新创建元组,例如发送会将字典值source : (x, y)转换为source_0 : x, source_1: y。我的第二个想法是,这不是一个非常明智的发展方式。

编辑:这是我使用JSON的实现......它看起来不像YAML或Pickle那么强大,但我仍然担心可能存在安全漏洞。

在我进行实验时,客户端的构建更加明显:

import urllib, json, base64

arbitrarydata = { 'id':14, 'gn':25, 'sourcecoord':(10,12), 'destcoord':(8,14)}

jsondata = json.dumps(arbitrarydata)
b64data = base64.urlsafe_b64encode(jsondata)
transmitstring = urllib.urlencode( [ ('data', b64data) ] )
urllib.urlopen('http://127.0.0.1:9000/post', transmitstring).read()

Pyramid Server可以检索数据对象:

json.loads(base64.urlsafe_b64decode(request.POST['data'].encode('ascii')))

在一个不相关的说明中,我很想听到关于在这种方法中使用POST数据的可接受性的其他意见,我的游戏客户端目前绝不是基于浏览器的。

3 个答案:

答案 0 :(得分:3)

json怎么样?该库是标准Python库的一部分,它允许序列化大多数通用数据,而无需任意执行代码。

答案 1 :(得分:3)

为什么不使用colander进行序列化和反序列化?漏勺将对象模式转换为简单的数据结构,反之亦然,您可以使用JSON发送和接收此信息。

例如:

import colander

class Item(colander.MappingSchema):
    thing = colander.SchemaNode(colander.String(),
                                validator=colander.OneOf(['foo', 'bar']))
    flag = colander.SchemaNode(colander.Boolean())
    language = colander.SchemaNode(colander.String()
                                   validator=colander.OneOf(supported_languages)

class Items(colander.SequenceSchema):
    item = Item()

上述设置定义了一个项目对象列表,但您也可以轻松定义特定于游戏的对象。

反序列化成为:

    items = Items().deserialize(json.loads(jsondata))

和序列化是:

    json.dumps(Items().serialize(items))

除了让您遍历python对象外,它还验证了序列化数据,以确保它符合您的架构,并且没有被篡改。

答案 2 :(得分:0)

我没有看到原始JSON提供答案,因为我认为这个问题特别提到了和值。我不相信使用直接JSON可以序列化和反序列化python类,而pickle可以。

我使用基于pickle的序列化方法进行几乎所有服务器到服务器的通信,但始终包含非常严格的身份验证机制(例如RSA密钥对匹配)。但是,这意味着我只处理可靠的来源。

如果您绝对需要使用不受信任的来源,我至少会尝试添加(很像@MartijnPieters建议的)架构来验证您​​的交易。我不认为有一种很好的方法可以处理来自不受信任来源的任意腌制数据。你必须做一些事情,比如用一些反汇编程序解析字节字符串,然后只允许可信模式(或阻止不受信任的模式)。我不知道有什么可以为pickle做到这一点。

但是,如果你的课程“足够简单”......你可以使用JSONEncoder,这实际上将你的python类转换为JSON可以序列化的东西......然后验证......

How to make a class JSON serializable

但是,您必须从JSONEncoder派生您的课程。