系统对象:TxContext 和避免安全漏洞
另一个非常常用的系统对象是 TxContext。我们已经看到它在两个主要用例中的应用:
- 使用
object::new
创建一个新对象的 id - 使用
tx_context::sender
获取发送者
public struct MyObject has key {
id: UID,
value_1: u64,
value_2: u64,
}
public fun create_object(value_1: u64, value_2: u64, ctx: &mut TxContext) {
let object = MyObject {
id: object::new(ctx),
value_1,
value_2,
};
transfer::transfer(object, tx_context::sender(ctx));
}
开发人员应该密切关注如何使用从 tx_context::sender
返回的发送者地址。
将新创建的对象发送到该地址是可以的,但将其用作认证或用户直接调用此函数的意图证明可能会有问题。例如:
module 0x123::safe_module {
public fun claim_rewards(amount: u64, receiver: address, ctx: &mut TxContext) {
let sender = tx_context::sender(ctx);
assert!(sender == @0x12345, ENOT_AUTHORIZED);
// 发送定额奖励到接收者地址
}
}
在上面的示例中,我们希望编写一个特殊函数 claim_rewards
,允许特定地址调用并提取一定数量的奖励金。
表面上看起来是安全的,但可能会被利用!恶意开发者可以编写一个模块,向用户承诺空投,并在代码中执行以下操作:
module 0x123::malicious_module {
const MALICIOUS_DEVELOPER: address = @0x98765;
public fun airdrop(ctx: &mut TxContext) {
safe_module::claim_rewards(1000, MALICIOUS_DEVELOPER, ctx);
}
}
这样会立即耗尽用户的奖励!safe_module
无法轻易区分 ctx 对象是由 VM(首次函数调用)传递的,还是由其他对象的另一个函数传递的。
一个解决方案是将 claim_rewards
设置为入口函数,这样它必须由用户直接调用。
然而,在某些情况下,如果我们希望自己的代码(来自同一包中的不同模块)调用此函数,这可能并不理想。
总的来说,使用 tx_context::sender
作为认证机制是有风险的,如果可能存在任何利用的可能性,应进行非常彻底的评估。
tx_context
模块提供的其他功能包括:
digest
:返回交易哈希epoch
和epoch_timestamp_ms
:返回当前的 epoch 编号及相应的时间戳fresh_object_address
:使用与 object::new 相同的底层函数生成新对象的地址