在 Axelar 上开发安全跨链应用

  • Ackee
  • 发布于 2023-04-06 19:27
  • 阅读 7

本文介绍了在 Axelar 协议上开发安全跨链应用程序的主要威胁、错误和最佳实践。重点包括 Axelar 架构、GMP 协议、 gas 支付方式、升级注意事项以及常见安全问题,如 AxelarExecutable 的继承、地址验证、重入风险等。同时强调了静态分析、单元测试和模糊测试在保障跨链应用安全中的重要性。

多链之间的通信变得越来越重要。尽管主要的安全性责任和信任在于所选择的桥协议,但在这些桥之上糟糕的跨链应用实现可能会导致灾难性的后果。本指南涵盖了在 Axelar 协议 上开发安全跨链应用的主要威胁、错误和最佳实践。

Axelar 架构

我们将主要关注应用层和网关,但对整个架构有基本的了解是很好的。

Axelar 在每个链上都部署了 AxelarGateway 合约,它充当应用程序和 Axelar 网络之间的中间层,并提供这些层之间的通信。在源链上,网关将消息(作为事件)发送给验证者,并最终销毁/锁定 token。在目标链上,网关负责接收到的消息验证,并最终铸造/解锁 token。Axelar 网络共识由一组使用委托权益证明的 验证者 实现。

Axelar 通用消息传递协议

Axelar GMP 是 Axelar 网络的基本功能,它为开发者提供了一种简单的方式来发送 token 并在受支持的链上调用函数。消息流是怎样的?

  1. 源链上的应用程序调用以下网关函数之一:

sendToken 仅发送 token,

callContract 在目标链上执行 payload

callContractWithToken 发送 token 并执行调用。

  1. 网关发出包含所有参数的事件。
  2. 验证者验证消息并通知目标网关。
  3. 在目标链上的应用程序中调用 execute 函数。

GMP Express

Axelar 可以允许特权客户端(协议)使用消息传递协议的 Express 版本。一条标准消息可能需要几分钟才能让 Axelar 网络完全批准该消息并将其传递到最终目的地。Express 消息仍然会通过 Axelar 网络,但在批准过程中,GMP Express 服务会将任何发送的 token 借给目标地址。一旦完成完整的批准,token 将被偿还给 GMP Express 服务。此过程可以将跨链传输速度提高十倍以上。GMP Express 的关键部分是特权协议和 Axelar 之间的完全信任。当收到标准消息时,特权协议 必须 实现 token 返回的逻辑。否则,特权协议将收到 2 倍的 token。

如何开始

在 Axelar 上开发应用程序简单明了,可以使用 Axelar GMP SDK。只需将 @axelar-network/axelar-gmp-sdk-solidity 依赖项添加到你的 Solidity 项目中,就可以开始了。

应用程序是 AxelarExecutable

@axelar-network/axelar-gmp-sdk-solidity/contracts/executables/AxelarExecutable.sol

每个构建在 Axelar 网络上的应用程序都必须继承自 AxelarExecutable 合约,该合约实现了 IAxelarExecutable 接口并处理接收到的消息。

contract YourApp is AxelarExecutable {

IAxelarGasService public immutable gasService;

constructor(
address gatewayAddress_,
address gasServiceAddress_,
) AxelarExecutable(gatewayAddress_) {
if (gatewayAddress_ == address(0)
|| gasServiceAddress_ == address(0)
) revert ZeroAddress();
gasService = IAxelarGasService(gasServiceAddress_);
}
}

接收消息

以下内部函数旨在在应用程序中被覆盖,以处理接收到的消息或/和 token。这也是应用程序安全的关键部分。请 最大限度 地注意,避免任何重入、逻辑错误或错误计算。

function _execute(
       string calldata sourceChain,
       string calldata sourceAddress,
       bytes calldata payload
   ) internal virtual

如果源链在网关上调用了 sendTokencallContractWithToken 函数,那么在目标链上的应用程序中,_executeWithToken 函数会被调用。铸造/解锁 token 由 gateway.validateContractCallAndMint 函数自动处理。

function _executeWithToken(
       string calldata sourceChain,
       string calldata sourceAddress,
       bytes calldata payload,
       string calldata tokenSymbol,
       uint256 amount
   ) internal virtual

发送消息

对于发送消息,有 IAxelarGateway,它是 Axelar 网关的接口,用于发送 token 和消息。

@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol

sendToken 函数提供多链 token 转移。目标链由字符串 id 标识,目标地址也是一个字符串。根据 token 的类型(内部/外部),它会在源链上被销毁/锁定,并在目标链上被铸造/解锁。

function sendToken(
       string calldata destinationChain,
       string calldata destinationAddress,
       string calldata symbol,
       uint256 amount
   )

对于发送跨链消息(payload),使用 callContract 函数。

function callContract(
       string calldata destinationChain,
       string calldata destinationContractAddress,
       bytes calldata payload
   )

如果你需要同时发送消息和 token,可以使用 callContract<code class="er lu lv lw lx b">WithToken 函数。

function callContractWithToken(
       string calldata destinationChain,
       string calldata destinationContractAddress,
       bytes calldata payload,
       string calldata symbol,
       uint256 amount
   )

如何在 Axelar 上支付 gas 费?

有两种方法可以为跨链调用支付 gas 费。首选的方法是 AxelarGasService 合约,它作为源链上目标链交易的预付款。

  1. 使用 AxelarJS SDK 在目标链上调用 estimateGasFee 函数来计算 gas 费。
  2. 将计算出的 gas 费作为 msg.value 从智能合约传递到源链上的 AxelarGasService 合约,使用以下函数之一:

payGasForContractCall

payGasForContractCallWithToke

payNativeGasForContractCall

payNativeGasForContractCallWithToken 目标链上的用户/应用程序也可以手动支付 gas 费。有关支付 gas 费的详细信息,请参阅 Axelar 文档 中的这篇文章。

Axelar跨链应用程序的例子

我们准备了一个示例跨链应用,用于通过 Axelar 网络发送消息和 token,可在 GitHub 上找到。该项目还包括 Wake 测试。

可升级性

对于可升级的合约,建议从 Axelar 的 Upgradable 合约继承,该合约实现了 IUpgradable 接口。

@axelar-network/axelar-gmp-sdk-solidity/contracts/upgradables/Upgradable.sol

然后定义一个唯一的 contractId 常量,该常量需要在合约版本之间保持不变。contractId 是应用程序/组件名称的哈希值,例如 keccak256("your-app")。在每次升级期间,Upgradable 实现逻辑会检查新实现的 contractId 是否与 Proxy 的 contractId 匹配。如果不匹配,则升级失败。

我们为 Wake 静态分析器 实现了一个 detector,它会检查 contractId 常量。如果实现的代理缺失,或者存在多个具有相同 contractId 的代理合约,它会发出警告。

需要注意的事项

除了了解 Solidity/EVM 的常见漏洞外,跨链应用程序的开发还带来了更多需要谨慎的主题。让我们更深入地探讨六个最重要的主题。

从 AxelarExecutable 继承

AxelarExecutable 合约包含针对 Axelar 网关的重要检查,以验证合约调用。绕过这些检查(例如直接实现 IAxelarExecutable 接口)可能会导致严重的漏洞。由于 execute 函数不受任何修饰符的保护,因此每个人都可以使用任何 payload 调用它,并在目标链上执行任何函数。

因此,请确保你的 execute 函数使用此网关函数 <code class="er lu lv lw lx b">validateContractCall 验证调用,该函数检查 Axelar 验证器是否确认了消息的来源和 payload 有效。

if (!gateway.validateContractCall(commandId, sourceChain, sourceAddress, payloadHash))
            revert NotApprovedByGateway();

类似的方法适用于 executeWithToken 函数,但要使用 validateContractCallAndMint

 if (!gateway.validateContractCallAndMint(
                commandId,
                sourceChain,
                sourceAddress,
                payloadHash,
                tokenSymbol,
                amount
            )
        ) revert NotApprovedByGateway();

Axelar 组件地址

Axelar 网关是协议安全的基础部分,因此在部署期间仔细检查传递给应用程序的网关地址非常重要。恶意的网关可能会操纵消息检查。从用户的角度来看,这一点也很重要。始终检查应用程序是否使用了 官方的 Axelar 网关和 Gas Service 地址

此外,应用程序不应包含网关地址设置器。这将降低对应用程序的信任。如果所有者可以控制网关地址,他也可以以多种方式操纵跨链消息。

目标链和地址验证

在将消息发送到另一个链之前,请对 destinationChaindestinationAddress 进行数据验证。将 token 发送到不存在的链或地址意味着资金的损失。

验证目标链上的源地址

避免接收来自任何源地址的消息非常重要。为此,将应用程序部署到多个链上的相同地址是一个好的做法。Axelar 为此目的提供了实用程序 ConstAddressDeployerCreate3Deployer。然后,你可以简单地使用此条件在 _execute_executeWithToken 函数中验证源地址。

if (sourceAddress.toAddress() != address(this)) revert InvalidSourceAddress()

Express GMP 数据验证

如果你决定使用 ExpressExecutable 合约,请 格外 小心接收消息的数据验证。函数 executeexecuteWithToken 是外部的,并且不受任何修饰符的保护,因此任何人都可以调用它们,并可能使用恶意 payload 以多种方式利用该合约。ExpressExecutable 中也缺少使用 Axelar 网关进行的消息验证。请记住这一点,并在此处实现强大的数据验证。

NFT 翻倍

在实现 NFT 跨链转移时,请确保用户的 NFT 已正确锁定在合约中,并且用户不能同时在两个链上拥有它。

静态分析、单元测试和模糊测试

合约开发不会随着编程本身而结束。当协议使用用户的资金或其他贵重物品进行操作时,质量保证必须是你的首要任务。开发团队可以在分配 安全审计 之前在内部执行这些步骤。

静态分析

使用 Slither、MythX、Mithrill 等经典静态分析工具在开发过程中检测可能的漏洞可能很有帮助,但由于许多误报,也会很痛苦。我们的 Wake 静态分析器 更深入地探讨了这个兔子洞。我们的目标是 最大限度 地减少误报检测,并在接受较低召回率的同时实现高精度。查看 Tools For Solidity 扩展,该扩展直接在 VSCode 中显示静态分析结果。

单元测试 Axelar

正确测试应用程序非常重要。单元测试对于测试所有用例很有用,并且高测试覆盖率对于基本安全至关重要。

由于 Axelar 上的应用程序在多个链上运行,因此它为系统带来了额外的复杂性,并且跨链测试 绝对应该 成为开发流程的一部分。为此,我们推荐我们易于使用的测试框架 Woke。它提供了一套完整的工具,用于 跨链测试 甚至 模糊测试。有关更多信息,请参阅 文档。另外,不要错过文章 使用开源工具测试 Axelar 合约

单元和模糊 测试覆盖率 也由 Wake 计算,并在 Tools For Solidity 扩展中显示。在模糊测试的情况下,覆盖率是实时计算的。

模糊测试 Axelar

模糊测试是一种用于测试软件的技术,它涉及提供无效、意外或随机的数据作为计算机程序的输入。在此处了解有关 Ackee Blockchain 的模糊测试工具和方法的更多信息 here

单元测试主要涵盖系统的预期行为和严格定义的用例。另一方面,模糊测试可以测试各种不可预测的、随机的场景,甚至可以发现隐藏的漏洞,例如不一致的计算。

总结

Axelar 为开发者提供了一个强大的平台和工具来开发跨链应用程序。Axelar 跨链应用程序继承了桥的安全性,但这并不意味着应用程序是安全的。在应用程序逻辑的实现过程中仍然存在潜在的陷阱。

即使桥是万无一失的,这并不意味着应用程序也是如此,并且用户的资金是安全的。始终进行 严格 的内部测试和独立的外部 安全审计,以实现高安全标准和 Web3 中最重要的事情之一 —— 社区信任。

  • 原文链接: ackee.xyz/blog/how-to-de...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Ackee
Ackee
Cybersecurity experts | We audit Ethereum and Solana | Creators of @WakeFramework , Solidity (Wake) & @TridentSolana | Educational partner of Solana Foundation