Skip to content

xiaomo1128/wallet-auth-server

Repository files navigation

aws Lambda Serverless deploy

serverless deploy --stage staging

serverless deploy --stage production # 部署到生产环境

serverless info --stage staging # 查看部署信息

Redis 是否正常启动

  • 本地开发:
# 快速检查
curl http://localhost:3001/health

# 详细调试
curl http://localhost:3001/health/detailed

# 只看 Redis
curl http://localhost:3001/health/redis
  • 生产环境监控:
# 监控系统调用
curl https://your-api.com/health

# 负载均衡器健康检查
GET /health

auth.service.ts 代码分析与优化建议

  1. Nonce 处理:
  • Nonce 生成后存储在 Redis 中,但没有与特定地址绑定,这可能导致任何人都可以使用这个 nonce
  • 建议将 nonce 与请求者的 IP 或其他标识符绑定

Redis是否需要定期调用某特殊接口缓存数据

  1. 签名验证过程:
  • 没有对传入消息格式进行严格验证
  • 建议使用结构化消息并遵循 EIP-191 或 EIP-712 标准
    • EIP-191: 签名数据标准 (Signed Data Standard)

      • 防止重放攻击:确保一个为特定目的签名的消息不能被用于另一个不相关的目的(例如,不能把一个普通消息签名当成交易签名)。
      • 版本化:为不同类型的签名数据提供了一个区分机制。
    • EIP-712: 以太坊类型化结构数据的哈希与签名 (Ethereum typed structured data hashing and signing)

      • EIP-712 是在 EIP-191 的基础上发展起来的一个更高级、更用户友好的签名标准。它旨在解决 eth_sign 带来的“盲签”问题——即用户签署的是一串难以理解的十六进制哈希,无法确切知道自己究竟同意了什么。
      • EIP-712 的核心思想是让用户在签名时能够看到清晰、结构化的、人类可读的数据内容。
      • 提升安全性:用户能明确知道自己签的是什么内容,大大降低了被钓鱼攻击的风险。
      • 提升用户体验:将复杂的数据以结构化、可读的方式呈现。
      • 防止跨应用/链的重放攻击:通过域分隔符确保签名的唯一上下文。
    • EIP-191 是一个基础的“信封”标准,告诉你这个信封里装的是什么类型的签名数据;而 EIP-712 则是一种具体的信件格式,让你能清楚地读懂信件的内容,并且知道这封信是写给谁的、用在什么场合的。

  1. 代码优化
  • 错误处理:

    • 有多处类似的错误处理代码,可以抽象为共用方法
    • 错误信息没有国际化
  • 代码结构:

    • extractNonceFromMessage 使用正则表达式,容易出现边界问题
    • 消息格式应当更加规范化并使用结构化数据
  • 性能优化:

    • Redis 操作没有异常处理机制
    • 多次数据库操作可以合并或使用事务
  1. 安全建议
  • 实现请求速率限制,防止暴力攻击
  • 添加日志脱敏处理,避免敏感信息泄露
  • 考虑增加双因素认证选项
  • 添加防重放攻击机制(例如,时间戳验证)
  1. 测试建议
  • 增加单元测试覆盖所有边界情况
  • 添加集成测试验证整个认证流程
  • 模拟恶意请求的安全测试

内存存储方式的问题和限制

private users: Map<string, User> = new Map();
private nonces: Map<string, { nonce: string; expires: Date }> = new Map();
  1. 数据不持久化 当服务器重启、崩溃或部署新版本时,所有存储的用户数据会完全丢失。这意味着:
  • 所有用户需要重新注册/登录
  • 历史数据无法恢复
  • 系统状态无法保持连续性
  1. 单实例限制 用户数据只存在于当前服务实例的内存中:
  • 无法进行水平扩展(多实例部署)
  • 如果部署多个实例,用户在A实例注册后,访问B实例时会被视为新用户
  • 负载均衡会导致用户状态不一致
  1. 内存占用问题 随着用户数量增长:
  • 内存占用持续增加
  • 可能导致服务器内存不足
  • 没有垃圾回收或数据过期机制
  1. 缺乏数据管理功能 与数据库相比:
  • 无法进行复杂查询(只能遍历全部数据)
  • 缺乏索引优化(查找效率低下)
  • 没有事务支持
  • 无法进行数据备份和恢复
  1. 安全隐患
  • 敏感数据明文存储在内存中
  • 缺乏访问控制和权限管理
  • 没有数据加密保护

这种实现方式只适合:

  • 开发和测试环境
  • 概念验证或原型
  • 极小规模的演示应用

在实际生产环境中,应当使用数据库(如MongoDB、MySQL、PostgreSQL等)替代内存存储,以确保数据的持久性、一致性和可扩展性。

钱包签名认证问题解决方案

根本原因分析

从您提供的日志中,我发现了两个主要问题:

  1. 最新的401错误原因:nonce提取失败

    [Nest] 17377  - 2025/05/21 17:38:03   DEBUG [AuthService] 从消息中提取的nonce: 0
    [Nest] 17377  - 2025/05/21 17:38:03    WARN [AuthService] 未找到对应的nonce: 0
    

    当前的正则表达式在处理0x前缀的十六进制nonce时只提取了第一个字符"0",导致nonce验证失败。

  2. 控制器调用错误的验证方法

    @Post('simple-verify')
    async verifySimpleSignature(...) {
      // 错误:调用verifySignature而不是verifySimpleSignature
      const result = await this.authService.verifySignature(message, signature, address);
      // ...
    }

    虽然您已经实现了verifySimpleSignature方法,但控制器仍在调用verifySignature方法。

完整解决方案

1. 修复nonce提取正则表达式

auth.service.ts中更新提取nonce的方法:

private extractNonceFromMessage(message: string): string | null {
  try {
    // 更新正则表达式,正确匹配0x开头的十六进制nonce
    const nonceMatch = message.match(/Nonce: (0x[a-f0-9]+|[a-f0-9]+)/i);
    if (nonceMatch && nonceMatch[1]) {
      this.logger.debug(`提取到的原始nonce字符串: '${nonceMatch[1]}'`);
      return nonceMatch[1];
    }
    return null;
  } catch (error) {
    this.logger.error(`提取nonce失败: ${error.message}`);
    return null;
  }
}

// 同样修复简单消息的nonce提取
private extractNonceFromSimpleMessage(message: string): string | null {
  try {
    const nonceMatch = message.match(/Nonce: (0x[a-f0-9]+|[a-f0-9]+)/i);
    if (nonceMatch && nonceMatch[1]) {
      this.logger.debug(`提取到的原始nonce字符串: '${nonceMatch[1]}'`);
      return nonceMatch[1];
    }
    return null;
  } catch (error) {
    this.logger.error(`从简单消息提取nonce失败: ${error.message}`);
    return null;
  }
}

2. 确保控制器使用正确的验证方法

auth.controller.ts中修改simple-verify路由:

@Post('simple-verify')
async verifySimpleSignature(
  @Body() body: { message: string; signature: string; address: string },
) {
  this.logger.debug(`收到简化验证请求: ${JSON.stringify(body, null, 2)}`);

  const { message, signature, address } = body;

  // 参数验证...

  this.logger.debug('开始简化签名验证...');

  // 修正:使用正确的verifySimpleSignature方法
  const result = await this.authService.verifySimpleSignature(
    message,
    signature,
    address,
  );

  // 结果处理...
}

3. 强化前端日志记录

在前端的AuthContext.tsx中添加更多日志:

const createSimpleSignMessage = async (address: string) => {
  const nonce = await fetchNonce();
  console.log('获取到的nonce:', nonce); // 确认nonce格式

  // 创建消息...
  const message = `Sign this message to authenticate with ${domain}.\n\nAddress: ${address}\nNonce: ${nonce}\nTimestamp: ${new Date().toISOString()}`;

  console.log('创建的简单消息:', message);
  return message;
};

const login = async () => {
  // ...

  // 添加更多日志以便调试
  console.log(
    '发送到后端的数据:',
    JSON.stringify(
      {
        message,
        signature,
        address: normalizedAddress,
      },
      null,
      2,
    ),
  );

  // ...
};

关键修复点总结

  1. 正则表达式处理:修改正则表达式以正确匹配带有0x前缀的十六进制nonce
  2. 方法调用一致性:确保控制器调用正确的验证方法
  3. 日志增强:添加更详细的日志记录,特别是在nonce提取和处理阶段
  4. 错误处理完善:添加更清晰的错误消息,便于调试

通过这些修改,您的钱包签名认证系统应该能够正常工作了。重启前后端服务后测试,您应该能够成功通过钱包签名完成认证流程。

About

Web3 Wallet signature login service

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published