python中的对象污染(原型污染)

.items()

.items() 是 Python 字典对象的一个方法,它返回一个包含字典所有键值对的视图对象。每个键值对是一个元组 (key, value)。这个方法在遍历字典时非常有用,因为它可以同时获取键和值。

示例
data = {
    "name": "Alice",
    "age": 30,
    "city": "Wonderland"
}

for key, value in data.items():
    print(f"Key: {key}, Value: {value}")

输出:

Key: name, Value: Alice
Key: age, Value: 30
Key: city, Value: Wonderland

在这个示例中,data.items() 返回一个包含所有键值对的视图对象,然后我们使用一个 for 循环来遍历这些键值对。

setattr()

setattr() 是一个内置函数,用于为对象设置属性。它接受三个参数:

  1. 对象:你要设置属性的对象。
  2. 属性名:你要设置的属性的名称(字符串形式)。
  3. 值:你要设置的属性的值。
示例
class Example:
    pass

example = Example()

# 动态设置属性
setattr(example, 'name', 'Alice')
setattr(example, 'age', 30)

print(example.name)  # 输出: Alice
print(example.age)   # 输出: 30

在这个示例中,setattr(example, 'name', 'Alice') 语句为 example 对象动态设置了一个名为 name 的属性,并赋值为 Alice。类似地,setattr(example, 'age', 30) 设置了一个名为 age 的属性,并赋值为 30

综合应用

结合使用 .items()setattr(),可以轻松地将字典中的键值对赋值给对象的属性。以下是一个综合示例:

class User:
    def __init__(self):
        self.username = None
        self.age = None

user = User()
data = {
    "username": "Alice",
    "age": 30
}

for key, value in data.items():
    setattr(user, key, value)

print(user.username)  # 输出: Alice
print(user.age)       # 输出: 30

在这个示例中,data.items() 返回字典 data 中所有键值对的视图对象,我们遍历这些键值对,并使用 setattr() 将每个键值对赋值给 user 对象的对应属性。

hasattr()

hasattr() 是 Python 的一个内置函数,用于检查对象是否具有某个属性。它返回一个布尔值:如果对象具有指定的属性,则返回 True,否则返回 False

语法

hasattr(object, name)
  • object:要检查的目标对象。
  • name:属性名,以字符串形式传递。

示例

以下是一些使用 hasattr() 的例子:

检查存在的属性
class User:
    def __init__(self, username, password):
        self.username = username
        self.password = password

user = User("Alice", "secret")

# 检查是否有属性 username 和 password
print(hasattr(user, "username"))  # 输出: True
print(hasattr(user, "password"))  # 输出: True
检查不存在的属性
class User:
    def __init__(self, username, password):
        self.username = username
        self.password = password

user = User("Alice", "secret")

# 检查是否有属性 age
print(hasattr(user, "age"))  # 输出: False

__init__

__init__ 方法是 Python 中的一个特殊方法,也被称为构造函数(constructor)。它在创建类实例时自动调用,用于初始化对象的属性。每当你创建一个类的实例时,__init__ 方法会被自动执行,从而为对象赋予初始状态。

下面是一个简单的示例,展示了如何定义和使用 __init__ 方法:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

# 创建 Person 类的实例
person1 = Person("Alice", 30)
person2 = Person("Bob", 25)

# 输出属性
print(person1.name)  # 输出: Alice
print(person1.age)   # 输出: 30
print(person2.name)  # 输出: Bob
print(person2.age)   # 输出: 25

getattr()

getattr() 是 Python 内置函数之一,用于访问对象的属性。它允许你通过属性名(字符串形式)来动态地获取对象的属性值。getattr() 的基本用法如下:

getattr(object, name[, default])
  • object:目标对象。
  • name:属性名,以字符串形式传递。
  • default(可选):如果属性不存在,返回的默认值。如果不提供该参数且属性不存在,会引发 AttributeError

示例

以下是一些使用 getattr() 的例子:

获取存在的属性
class User:
    def __init__(self, username, password):
        self.username = username
        self.password = password

user = User("Alice", "secret")

# 使用 getattr 获取属性
username = getattr(user, "username")
password = getattr(user, "password")

print(username)  # 输出: Alice
print(password)  # 输出: secret
获取不存在的属性
class User:
    def __init__(self, username, password):
        self.username = username
        self.password = password

user = User("Alice", "secret")

# 尝试获取不存在的属性
try:
    age = getattr(user, "age")
except AttributeError:
    age = None

print(age)  # 输出: None
使用默认值
class User:
    def __init__(self, username, password):
        self.username = username
        self.password = password

user = User("Alice", "secret")

# 使用默认值
age = getattr(user, "age", 25)

print(age)  # 输出: 25

字典与对象

在Python中,字典和对象(类实例)虽然是不同的数据结构,但是在某些情况下,你可能需要将字典的键值对赋予给对象的属性。这通常在动态生成对象或根据某些配置动态设置对象属性时非常有用。

通过setattr将字典的键值对赋予给对象

在Python中,可以使用内置函数setattr将字典的键值对赋予给对象的属性。以下是一个简单的示例:

class Example:
    pass

example = Example()
data = {
    "name": "Alice",
    "age": 30,
    "city": "Wonderland"
}

for key, value in data.items():
    setattr(example, key, value)

print(example.name)  # 输出: Alice
print(example.age)   # 输出: 30
print(example.city)  # 输出: Wonderland

在这个示例中,我们定义了一个空类Example,然后创建了一个实例example。通过遍历字典data,我们使用setattr函数将每个键值对赋予给example对象的属性。

合并操作中的应用

合并操作不仅仅是将字典的键值对赋予给对象,还可能包含嵌套结构的合并。以下是一个完整的例子,展示了如何将源字典的内容递归地合并到目标对象中:

class User:
    def __init__(self):
        self.username = None
        self.password = None
        self.profile = {"age": None, "city": None}

def merge(src, dst):
    for k, v in src.items():
        if k in ["__proto__", "constructor", "prototype"]:
            # 跳过原型污染相关的特殊键
            continue
        if isinstance(dst, dict):  
	        # 目标是字典
            if k in dst and isinstance(dst[k], dict) and isinstance(v, dict):
                # 如果目标字典中存在相同键且对应的值也是字典,则递归合并
                merge(v, dst[k])
            else:
                # 直接赋值
                dst[k] = v
        else:
            # 目标是对象,使用setattr
            if hasattr(dst, k) and isinstance(getattr(dst, k), dict) and isinstance(v, dict):
                merge(v, getattr(dst, k))
            else:
                setattr(dst, k, v)

# 示例数据
data = {
    "username": "Alice",
    "password": "secret",
    "profile": {
        "age": 30,
        "city": "Wonderland"
    }
}

user = User()
merge(data, user)

print(user.username)  # 输出: Alice
print(user.password)  # 输出: secret
print(user.profile)   # 输出: {'age': 30, 'city': 'Wonderland'}

getattr() 的使用主要是为了检查对象的属性是否存在并且是否是字典,从而决定是否递归调用 merge 函数:

if hasattr(dst, k) and isinstance(getattr(dst, k), dict) and isinstance(v, dict):
    merge(v, getattr(dst, k))

这段代码的意思是:

  1. hasattr(dst, k):检查对象 dst 是否有属性 k
  2. isinstance(getattr(dst, k), dict):检查属性 k 是否是字典。
  3. isinstance(v, dict):检查 src 的值 v 是否是字典。

如果这些条件都满足,则递归调用 merge 函数,将 src 的嵌套字典 v 合并到 dst 对象的字典属性 k 中。

通过这种方式,你可以动态地处理对象的属性,而无需显式地知道这些属性的名称。

对象污染

Python 中的 “原型污染” 并不是一个常见的术语,更常见的说法是 “属性污染” 或 “对象污染”。这一类问题通常出现在允许用户输入不受控制地影响对象属性的情况下。这种漏洞可能会导致安全问题,例如执行任意代码、数据泄露或者服务中断。

示例

class User:
    def __init__(self, username, password):
        self.username = username
        self.password = password

def update_user(user, data):
    for key, value in data.items():
        setattr(user, key, value)

user = User("Alice", "secret")

user_input = {
    "username": "Bob",
    "__dict__": {"malicious": "data"}
}

update_user(user, user_input)

print(user.__dict__)

在这个示例中,用户输入的 user_input 包含一个特殊的 __dict__ 键,它试图直接修改对象的内部字典,从而添加了一个新的属性 malicious

{'username': 'Bob', 'password': 'secret', 'malicious': 'data'}

具体执行过程

  1. 初始化对象

    user = User("Alice", "secret")
    

    此时,user 对象的内部字典 __dict__ 内容是:

    {'username': 'Alice', 'password': 'secret'}
    
  2. 调用 update_user 函数

    update_user(user, user_input)
    
  3. update_user 函数中循环遍历 user_input 字典

    • 对于 key = "username"value = "Bob":

      setattr(user, "username", "Bob")
      

      此时,user.__dict__ 变为:

      {'username': 'Bob', 'password': 'secret'}
      
    • 对于 key = "__dict__"value = {"malicious": "data"}:

      setattr(user, "__dict__", {"malicious": "data"})
      

      这相当于直接执行:

      user.__dict__ = {"malicious": "data"}
      

      因此,user.__dict__ 最终变为:

      {'malicious': 'data'}
      

结果

最终,user 对象的内部字典被完全替换为新的字典,原来的所有属性都丢失了,只剩下新的 malicious 属性。

常用魔术方法

Python 中的魔术方法(也称为 dunder 方法,double underscore methods)是以双下划线 __ 开头和结尾的方法。这些方法为类提供了特定的功能和行为,使得对象可以集成到 Python 的内置操作中。以下是一些常用的魔术方法及其示例。

1. __init__: 初始化方法

用于初始化对象的属性。

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

p = Person("Alice", 30)
print(p.name, p.age)

2. __str__: 字符串表示

用于定义对象的字符串表示,用于 printstr()

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return f"Person(name={self.name}, age={self.age})"

p = Person("Alice", 30)
print(p)  # 输出: Person(name=Alice, age=30)

3. __repr__: 官方字符串表示

用于提供对象的官方字符串表示,用于调试和开发者使用。

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __repr__(self):
        return f"Person(name={self.name}, age={self.age})"

p = Person("Alice", 30)
print(repr(p))  # 输出: Person(name=Alice, age=30)

4. __len__: 返回长度

使对象能够与 len() 函数一起使用。

class MyList:
    def __init__(self, items):
        self.items = items

    def __len__(self):
        return len(self.items)

my_list = MyList([1, 2, 3])
print(len(my_list))  # 输出: 3

5. __getitem__: 获取元素

使对象能够使用索引操作。

class MyList:
    def __init__(self, items):
        self.items = items

    def __getitem__(self, index):
        return self.items[index]

my_list = MyList([1, 2, 3])
print(my_list[1])  # 输出: 2

6. __setitem__: 设置元素

使对象能够使用索引进行赋值操作。

class MyList:
    def __init__(self, items):
        self.items = items

    def __setitem__(self, index, value):
        self.items[index] = value

my_list = MyList([1, 2, 3])
my_list[1] = 10
print(my_list.items)  # 输出: [1, 10, 3]

7. __delitem__: 删除元素

使对象能够使用索引进行删除操作。

class MyList:
    def __init__(self, items):
        self.items = items

    def __delitem__(self, index):
        del self.items[index]

my_list = MyList([1, 2, 3])
del my_list[1]
print(my_list.items)  # 输出: [1, 3]

8. __contains__: 检查包含

使对象能够与 in 操作符一起使用。

class MyList:
    def __init__(self, items):
        self.items = items

    def __contains__(self, item):
        return item in self.items

my_list = MyList([1, 2, 3])
print(2 in my_list)  # 输出: True

9. __call__: 可调用对象

使对象能够像函数一样被调用。

class Greeter:
    def __init__(self, greeting):
        self.greeting = greeting

    def __call__(self, name):
        return f"{self.greeting}, {name}!"

greeter = Greeter("Hello")
print(greeter("Alice"))  # 输出: Hello, Alice!

10. __iter____next__: 迭代器协议

使对象能够与迭代协议一起使用。

class MyRange:
    def __init__(self, start, end):
        self.start = start
        self.end = end
        self.current = start

    def __iter__(self):
        return self

    def __next__(self):
        if self.current >= self.end:
            raise StopIteration
        self.current += 1
        return self.current - 1

for number in MyRange(1, 4):
    print(number)  # 输出: 1, 2, 3
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值