【问题标题】:Serializing data and unpacking safely from untrusted source序列化数据并从不受信任的来源安全地解包
【发布时间】:2012-10-07 16:47:40
【问题描述】:

我正在使用 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 数据的可接受性的意见,我的游戏客户端目前绝不是基于浏览器的。

【问题讨论】:

  • 确实,您应该 永远对不受信任的数据使用泡菜。见zopatista.com/plone/2007/11/09/one-cookie-please
  • @MartijnPieters 我理解这种堆栈语言令人难以置信的有用性......在百万分之一的项目中。是不是有一些泡菜的子集不允许那些特定的操作员?在一个非常有用的系统中解决一个可怕的缺陷似乎让很多人头疼。
  • 不,没有。任意 python 对象意味着攻击者可以构造允许访问的东西。例如,Python stdlib 中有强大的基本服务器可用,它们会在实例化时打开端口。

标签: python yaml urllib2 pyramid pickle


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    为什么不使用colander 进行序列化和反序列化? 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 对象之外,它还验证序列化数据以确保它适合您的架构并且没有被弄乱。

    【讨论】:

      【解决方案3】:

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

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

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

      但是,如果您的类“足够简单”……您也许可以使用JSONEncoder,它实质上将您的python 类转换为JSON 可以序列化的东西……从而验证……

      How to make a class JSON serializable

      但是,影响是,您必须从 JSONEncoder 派生您的类。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-10-03
        • 1970-01-01
        • 2020-02-25
        • 2019-12-19
        • 2015-04-30
        • 2011-05-20
        相关资源
        最近更新 更多