本文介绍了在智能合约开发中使用加密密钥的重要性,并提供了在 Hardhat 和 Foundry 框架中实现加密密钥的详细步骤。文章强调了明文存储密钥的风险,并提供了使用 AES 等加密技术保护密钥的方法,同时推荐了密钥管理和安全开发的最佳实践。
想象一下,由于一个泄露的私钥而损失了价值数百万美元的加密资产。对于一些DeFi项目来说,这个噩梦已经成为了现实——最值得注意的是 $6.25亿美元的Ronin Bridge攻击,攻击者利用泄露的验证器密钥来耗尽资金——突显了开发者最危险的盲点:凭证管理。
虽然.env
文件是管理本地密钥的常用解决方案,但它们仍然存在固有的风险,因为它们以纯文本形式存储凭证,这些凭证可能会被意外地提交到版本控制中,或者通过错误配置暴露出来。为了更强的安全性,加密密钥存储是首选的替代方案,像Hardhat和Foundry这样的主要框架现在都提供了内置支持。
本指南将引导你如何在开发工作流程中实施加密密钥。你将了解这些加密系统是如何工作的,以及如何将它们与Hardhat和Foundry框架集成。无论你部署到测试网还是主网,你都会发现保护你的私钥在整个开发生命周期中的最佳实践。
在智能合约开发和部署期间,通常需要私钥和RPC URL等敏感凭据。这些实际上是你整个应用程序的密钥——如果暴露,攻击者可以耗尽你的钱包,接管基础设施,或冒充你的dApp。
虽然在早期开发或黑客马拉松期间很容易低估风险,但现实世界的事件表明,一个小小的错误——比如在公共GitHub仓库中留下一个密钥——可能会是灾难性的。
.env
文件根据Chainalysis 2024年的报告,私钥泄露占去年所有被盗加密货币的43.8%。其中最大的一起是3.05亿美元的DMM比特币黑客攻击,很可能是由于不正确的密钥存储或运营安全造成的。
Ronin Bridge攻击(6.25亿美元): 攻击者使用泄露的验证器密钥耗尽了迄今为止最大的DeFi桥之一的资金。 (来源)
$4万GitHub泄露(2024): 一名开发人员不小心将他们的私钥留在了一个私有的GitHub仓库中,他们将其短暂地公开,以提交一份授权申请。瞬间,他们的钱包被盗走了4万美元。 (来源)
Solana solana-web3.js
后门 (2024): 一个被广泛使用的JavaScript库的恶意更新引入了一个后门,可以直接从磁盘窃取私钥。在漏洞被发现之前,成千上万的项目受到了影响。 (来源)
这些不是个别情况——它们代表了一种趋势。密钥管理仍然是Web3中最大的攻击媒介之一。
当你加密一个密钥时,你正在使用密钥或密码短语将其转换为密文。只有具有正确解密密钥的用户或系统才能检索原始值。
在像Hardhat和Foundry这样的框架中:
secrets.json
,钱包密钥库等)即使你的机器被入侵或者仓库错误地公开了,加密密钥也会增加一个关键的屏障。如果没有解密密码,攻击者就不能简单地读取原始密钥。仅凭这一点就可以防止资金被盗,尤其是来自自动化机器人的盗窃。
在深入了解特定于框架的加密密钥的指令之前,让我们先准备好你的环境。此设置将允许你使用Hardhat或Foundry顺利地遵循本指南。
要与任何区块链进行交互,你需要一个RPC端点。你可以使用QuickNode立即创建一个。在本指南中,我们将使用Etherem Sepolia作为测试网。如果你愿意使用不同的网络,请随时这样做。
你还需要Sepolia测试ETH来部署合约并支付交易费用。你可以从QuickNode Sepolia Faucet请求测试ETH (注意: 你必须在Ethereum Mainnet上至少有0.001 ETH才能使用 faucet,以避免垃圾邮件和滥用)
一旦你有了资金充足的测试网钱包和一个可用的端点,你就可以测试加密密钥了。
更喜欢Foundry?
如果你正在使用Foundry,你可以直接跳到 如何在Foundry中使用加密钱包 部分。
Hardhat 3 (目前处于alpha阶段) 通过内置的 secrets manager引入了加密密钥。此功能支持安全地存储任何基于字符串的密钥——包括私钥、RPC URL以及你可能需要的任何其他密钥。
warning
Hardhat 3 目前处于alpha阶段。在正式发布为稳定版本之前,请谨慎使用。
运行以下命令来创建一个新的Hardhat项目。
请注意,@next
被添加到 npx
命令中,以确保你使用的是带有 next
标签的Hardhat版本,截至本文撰写时,该版本是 Hardhat 3.0.0-next.4
。
mkdir secure-hardhat && cd secure-hardhat
npm init -y
npx hardhat@next --init
接受提示的默认答案。然后,验证Hardhat 3是否已安装:
npx hardhat --version
要存储你的RPC URL,运行以下命令:
npx hardhat keystore set SEPOLIA_RPC_URL
如果这是你第一次使用密钥库,系统会提示你输入密码。此密码将用作主密钥来加密你的密钥。这意味着如果你丢失了密码,你将无法访问你的密钥。
然后,系统会提示你输入一个值,该值将使用你提供的主密钥进行加密。
通过运行以下命令,对你的私钥重复此过程:
npx hardhat keystore set SEPOLIA_PRIVATE_KEY
要验证你的密钥是否已加密,运行以下命令:
npx hardhat keystore list
你应该在加密密钥的列表中看到你的RPC URL和私钥。
如果需要再次获取密钥值,可以运行以下命令。这将提示你输入主密钥并解密该值:
npx hardhat keystore get SEPOLIA_RPC_URL
设置好密钥后,你可以在配置文件中引用它们,以便在项目中安全地使用它们。
当你初始化一个新的Hardhat项目时,它会附带一个预配置的hardhat.config.js
文件,以及一个示例合约,部署脚本和一个Hardhat Ignition部署文件 - 推荐的智能合约部署方式。
在本指南中,我们将坚持使用基本的脚本部署,但了解如何使用配置文件来引用你的密钥是有帮助的。
打开 hardhat.config.js
并检查 networks
对象以引用你加密的密钥。如果你使用的是不同的密钥名称,请相应地更新 networks
对象。
import { configVariable } from "hardhat/config";
module.exports = {
networks: {
sepolia: {
url: configVariable("SEPOLIA_RPC_URL"),
accounts: [configVariable("SEPOLIA_PRIVATE_KEY")],
},
},
};
就是这样!现在你可以在你的部署脚本中使用你加密的密钥,而永远不会以纯文本形式暴露它们。
运行以下命令来部署你的合约,使用 ignition/modules
下的 Counter.ts
模块, 该模块基本上部署了 Counter.sol
合约,然后使用值5调用 incBy
函数。
npx hardhat ignition deploy --network sepolia ignition/modules/Counter.ts
当你运行命令时,Hardhat会提示你输入之前设置的密码,因为sepolia
网络被配置为使用密钥库。只有当当前脚本或任务正在使用加密密钥时,才需要输入此密码。否则,你不会被提示输入密码。
输入密码后,Hardhat将部署你的合约并使用值5调用 incBy
函数。
祝贺你!你已成功在Hardhat中设置了加密密钥,并在你的部署脚本中使用了它们。
Foundry 支持通过 cast wallet
CLI 进行加密钱包导入。虽然它目前不支持加密像RPC URL这样的其他变量,但你仍然可以使用加密密钥和环境变量的组合来保护你的工作流程。
如果你是Foundry的新手,我们建议从 Foundry简介 指南开始,以熟悉基础知识。
如果尚未安装Foundry,请通过运行以下命令进行安装:
curl -L https://foundry.paradigm.xyz | bash
然后,通过运行以下命令来初始化Foundry:
foundryup
forge init secure-foundry
cd secure-foundry
此命令将创建一个名为 secure-foundry
的新目录,并在其中从默认模板初始化一个新的Foundry项目。
├── README.md
├── foundry.toml
├── lib
│ └── forge-std
├── script
│ └── Counter.s.sol
├── src
│ └── Counter.sol
└── test
└── Counter.t.sol
现在,我们将导入一个钱包以在我们的Foundry项目中使用。
要导入钱包,我们将使用 cast wallet
CLI。将 your-wallet-name
替换为你的钱包名称,并运行以下命令:
cast wallet import your-wallet-name --interactive
系统将提示你输入你的私钥并使用密码对其进行加密。密钥安全地存储在你本地的密钥库中,其默认路径为 ~/.foundry/keystore
。
使用
--interactive
标志的原因是为了避免将私钥存储在终端历史记录中。
当你的钱包被导入后,就可以使用了。
现在你的钱包已被加密,你需要安全地引用你的RPC端点。由于Foundry目前不支持加密任意值(如RPC URL),最实际的方法是使用.env
文件来管理这些密钥。虽然没有加密,但.env
文件仍然被广泛使用,并且比直接在源代码中存储密钥更安全。
tip
如果你正在寻找更大应用程序或团队中的额外保护,你还可以探索诸如 Dotenv Vault 之类的工具,用于加密的 .env
文件共享,或诸如 AWS Secrets Manager 之类的云管理解决方案,用于生产部署。
在你的项目的根目录中创建一个 .env
文件,并添加你的QuickNode端点:
SEPOLIA_RPC_URL=https://your-quicknode-endpoint
并在运行你的脚本之前加载它:
source .env
我们在前面的步骤中安全地导入了钱包,并将RPC端点添加到了我们的配置中。现在,我们可以运行我们的脚本并部署我们的合约。
Foundry的模板项目附带一个示例脚本,该脚本部署了一个 Counter
合约。更新脚本以使用你的钱包和RPC端点:
forge script script/Counter.s.sol:CounterScript --rpc-url $SEPOLIA_RPC_URL --account your-wallet-name --broadcast
终端将提示你输入用于加密私钥的密码。
输入密码后,Foundry将运行你的脚本并部署你的合约。
[⠊] Compiling...
No files changed, compilation skipped
Enter keystore password:
Script ran successfully.
### Setting up 1 EVM.
==========================
...
祝贺你!你已成功在Foundry中设置了加密密钥,并在你的部署脚本中使用了它们。
加密密钥是基础步骤,但维护安全开发工作流程需要额外的注意。以下是帮助保护凭据并最大程度地减少暴露风险的关键实践:
步骤 1: 使用 .gitignore
防止意外提交
如果你使用 .env
文件,请确保将它们从版本控制中排除,并将其保存在本地环境中。这些文件中的密钥未加密,应谨慎对待。
## Common entries
.env
secrets.json
foundry.keystore
步骤 2: 避免将密钥直接输入到终端中
使用交互式CLI提示或环境变量输入密钥。避免将密钥作为内联参数传递,以防止它们存储在shell历史记录中。
步骤 3:在出错后清除Shell历史记录 如果密钥意外地输入或粘贴到终端中,请立即清除你的历史记录:
history -c && history -w # 对于 Bash 和 Zsh
步骤 4: 为每个项目使用专用密钥
为不同的项目或环境生成单独的私钥和RPC端点URL。这限制了任何妥协的有效性。
tip
有关RPC端点安全性的更多详细信息,请查看 本指南。
步骤 5: 在部署之前测试密钥工作流程
始终验证你的项目是否在本地或暂存环境中正确解密密钥。
步骤 6: 保护你的本地机器
保持你的操作系统和工具更新。避免安装未验证的软件,并定期扫描可能针对钱包或密钥文件的恶意软件。
步骤 7: 探索加密密钥管理工具
步骤 8: 在生产中使用云密钥管理器
对于生产基础设施,请考虑使用基于云的密钥管理工具,例如:
私钥管理仍然是构建安全dApp和智能合约的最关键方面之一。随着Hardhat和Foundry现在都提供加密密钥支持,开发人员拥有保护敏感数据的工具,而不会损害速度或便利性。
无论你使用的是Hardhat的内置密钥管理器还是Foundry的加密钱包密钥库,尽早采用这些实践都有助于防止常见错误并保护你的部署。
如果你遇到问题或有疑问,请在我们的 Discord 中提出。通过在 Twitter (@QuickNode) 或我们的 Telegram announcement channel 上关注我们来了解最新信息。
如果你对新主题有任何反馈或要求,请 告诉我们。我们很乐意听到你的声音。
- 原文链接: quicknode.com/guides/web...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!