如何使用安全加载反序列化带有PyYAML的对象?

2024-04-27 09:11:35 发布

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

有这样一个片段:

import yaml
class User(object):
    def __init__(self, name, surname):
       self.name= name
       self.surname= surname

user = User('spam', 'eggs')
serialized_user = yaml.dump(user)
#Network
deserialized_user = yaml.load(serialized_user)
print "name: %s, sname: %s" % (deserialized_user.name, deserialized_user.surname)

Yaml docs说,使用从不受信任的源接收的任何数据调用yaml.load是不安全的;那么,我应该修改我的snippet\class以使用safe\u load方法吗?
有可能吗?


Tags: nameimportselfyamlobjectinitdefload
3条回答

根据定义,safe_load似乎不允许反序列化您自己的类。如果你想安全,我会这样做:

import yaml
class User(object):
    def __init__(self, name, surname):
       self.name= name
       self.surname= surname

    def yaml(self):
       return yaml.dump(self.__dict__)

    @staticmethod
    def load(data):
       values = yaml.safe_load(data)
       return User(values["name"], values["surname"])

user = User('spam', 'eggs')
serialized_user = user.yaml()
print "serialized_user:  %s" % serialized_user.strip()

#Network
deserialized_user = User.load(serialized_user)
print "name: %s, sname: %s" % (deserialized_user.name, deserialized_user.surname)

这里的优点是您可以绝对控制类的(反)序列化方式。这意味着您不会通过网络获得随机可执行代码并运行它。缺点是您可以绝对控制类的(反)序列化方式。这意味着你得做更多的工作。;-)

如果您有许多标记,但不想为所有标记创建对象,或者如果您不关心返回的实际类型,只关心点式访问,则可以使用以下代码捕获所有未定义的标记:

import yaml

class Blob(object):
    def update(self, kw):
        for k in kw:
            setattr(self, k, kw[k])

from yaml.constructor import SafeConstructor

def my_construct_undefined(self, node):
    data = Blob()
    yield data
    value = self.construct_mapping(node)
    data.update(value)

SafeConstructor.add_constructor(None, my_construct_undefined)


class User(object):
    def __init__(self, name, surname):
        self.name= name
        self.surname= surname

user = User('spam', 'eggs')
serialized_user = yaml.dump(user)
#Network
deserialized_user = yaml.safe_load(serialized_user)
print "name: %s, sname: %s" % (deserialized_user.name, deserialized_user.surname)

如果您想知道为什么my_construct_undefined中间有一个yield:它允许单独实例化对象,而不创建其子对象。一旦对象存在,就可以引用它,以防它有一个锚,而子对象(或其子对象)有一个引用。创建对象的实际机制首先创建它,然后对它执行next(x)操作来完成它。

另一种方式存在。来自PyYaml文档:

A python object can be marked as safe and thus be recognized by yaml.safe_load. To do this, derive it from yaml.YAMLObject [...] and explicitly set its class property yaml_loader to yaml.SafeLoader.

您还必须设置yaml_tag属性才能使其工作。

YAMLObject使用一些元类魔术使对象可加载。注意,如果这样做,对象将只能由安全加载程序加载,而不能使用常规的yaml.load()加载。

工作示例:

import yaml

class User(yaml.YAMLObject):
    yaml_loader = yaml.SafeLoader
    yaml_tag = u'!User'

    def __init__(self, name, surname):
       self.name= name
       self.surname= surname

user = User('spam', 'eggs')
serialized_user = yaml.dump(user)

#Network

deserialized_user = yaml.safe_load(serialized_user)
print "name: %s, sname: %s" % (deserialized_user.name, deserialized_user.surname)

这个方法的优点是很容易实现;缺点是它只在安全加载的情况下工作,并且用序列化相关的属性和元类扰乱类。

相关问题 更多 >