You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何验证MetaMask生成的以太坊签名?Rust后端实现方案

MetaMask签名的Rust后端验签实现方案

核心注意事项(90%新手踩坑点)

MetaMask调用web3.eth.personal.sign接口时,不会直接对传入的原始字符串签名,会严格按照EIP-191规范给消息拼接固定前缀:\x19Ethereum Signed Message:\n + 消息内容的字节长度,所有验签逻辑必须匹配这个前缀规则,否则会100%出现验签失败。

最优Crate选型

当前生态下最适合这类场景的是以太坊核心团队维护的alloy系列依赖:

  • 纯Rust实现,无C原生绑定,安装、编译、跨平台部署无额外依赖问题
  • 内置完整的EIP-191规则处理、签名解析、地址恢复逻辑,不需要手动拼接前缀、计算哈希
  • 提供常量时间比较的安全实现,避免时序侧信道攻击
    不推荐直接用底层secp256k1裸库手动实现,非常容易在字节长度计算、前缀格式拼接上出bug。

可直接运行的代码示例

第一步:配置Cargo.toml依赖

[dependencies]
alloy = { version = "0.3", features = ["signers", "eips"] }
anyhow = "1.0" # 简化错误处理的辅助依赖

第二步:核心验签逻辑

use alloy::primitives::Address;
use alloy::eips::eip191::eip191_hash_message;
use alloy::signers::utils::recover_signer;

/// 验证MetaMask签名的有效性
/// 参数说明:
/// - original_message: 前端传给MetaMask签名的原始字符串,前后端必须完全一致
/// - signature_str: MetaMask返回的0x开头的签名字符串
/// - user_input_address: 前端提交的用户MetaMask钱包地址
fn verify_metamask_sign(
    original_message: &str,
    signature_str: &str,
    user_input_address: &str,
) -> anyhow::Result<bool> {
    // 解析用户提交的地址为标准以太坊地址格式
    let expected_address = user_input_address.parse::<Address>()?;

    // 自动按照EIP-191规则计算实际被签名的消息哈希,无需手动拼接前缀
    let message_hash = eip191_hash_message(original_message.as_bytes());

    // 解析MetaMask返回的签名字符串为结构化签名对象
    let signature = signature_str.parse::<alloy::primitives::Signature>()?;

    // 从签名中恢复出实际签名对应的钱包地址
    let recovered_address = recover_signer(&signature, message_hash)?;

    // 常量时间比较两个地址,防止时序攻击
    Ok(recovered_address == expected_address)
}

生产环境补充建议

  • 前后端传输的签名消息必须使用后端生成的、带一次性随机nonce、设置短过期时间的字符串,验签通过后标记nonce为已使用,避免重放攻击
  • 不要在验签逻辑中手动拼接EIP-191前缀,优先依赖成熟库的内置实现,减少自定义逻辑的bug概率
  • 前后端传输的原始消息必须完全一致,哪怕差一个空格、一个标点、字符编码不一致(比如前端用UTF-16转码)都会导致验签失败

内容的提问来源于stack exchange,提问作者Jack Klimov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.24 02:57:00