前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >web3服务端身份验证

web3服务端身份验证

作者头像
Tiny熊
发布2022-04-11 13:41:00
2.3K0
发布2022-04-11 13:41:00
举报
文章被收录于专栏:深入浅出区块链技术

本文作者:影无双[1]

DApp 最大的吸引力就是用户拥有自己的数据。然而要做到这一点,需要验证用户的 web3 身份(用户的钱包)。这在客户端是很容易的(因为用户可以用 Metamask 提交自己的信息),但是在服务端就没那么容易了。

在这篇文章中,我将概述“钱包登录”按钮的技术实现,类似Showtime[2]或者Foundation[3]的按钮。

从钱包到服务端

第一部分实现非常简单,让用户将钱包连接到我们的前端,并且从获取的钱包地址向服务端发送一个 API 请求。

这里的问题是,任何人都可以用别人的地址向我们发送 API 请求,并且我们无法验证这个地址是否映射到与前端的钱包。

在服务端验证签名

容易忽略的一点,本质上加密钱包只是一个密钥对(私钥和公钥的组合)。当你创建一笔交易,你仅仅是签署了交易参数(以数学方式证明你是创建者)并且将它广播到 ETH 网络上。

幸运的是,交易并不是钱包唯一可以签名的东西。我们可以创建任意一条消息(如Please sign this message to connect to Foundation.),并且验证签名,以确保验证身份的钱包就是签署消息的钱包。

以太坊签名是以Ethereum Signed Message:开头的 Keccak (SHA-3)哈希。我们可以在任何程序语言中用 Keccak 和 ECC (椭圆曲线密码学) 库进行验证。

我们需要三样东西来验证:要验证的地址、要签名的消息和签名,我们可以用任何 web3 库获取签名(下面例子用的ethers.js ):

代码语言:javascript
复制
import axios from 'axios'
import { ethers } from 'ethers'

// On production, you should use something like web3Modal
// to support additional wallet providers, like WalletConnect
const web3 = new ethers.providers.Web3Provider(window.ethereum)

const message = "Sign this message to log in to our app"

await axios.post('/api/auth/login', {
 address: await web3.getSigner().getAddress(),
 signature: await web3.getSigner().signMessage(message),
})

在服务端,我们可以用eth-sig-util来验证被提交钱包所签名的消息,并且通过 cookie 或者 API token 来验证。

代码语言:javascript
复制
import { recoverPersonalSignature } from 'eth-sig-util'

const message = "Sign this message to log in to our app"

if (address.toLowerCase() !== recoverPersonalSignature({ data: data, sig: signature }).toLowerCase()) {
 throw new Error('Authentication failed')
}

// wallet address has been verified, set a cookie (or return a token)

如果你想更好的掌握验证背后是如何工作的,你可以查看 我的签名验证的 PHP 实现[4]

防止签名被利用

我们有一个可以用钱包登录的系统,和一套确保只能本人验证的方法。但是有一个问题,因为我们总是签名相同的消息,任何一个签名都是账户的永久密钥,永不过期。

这意味着,如果有人通过 MITM 攻击或欺骗我们在别的网站签署相同的消息来拦截它,他们将获得不可撤销的永久访问权限。

为了防止这样的事情发生,我们需要确保每次的消息都不同。最简单的方法就是生成一个随机字符串(nonce)包含到消息中。

我们首先需要在服务端生成 nonce ,并将其存储在会话中(因为之后需要它来验证签名):

代码语言:javascript
复制
import crypto from 'crypto'

export default async function(req, res) {
 req.session.nonce = crypto.randomInt(111111, 999999)

 res.end(`Hey! Sign this message to prove you have access to this wallet. This won't cost you anything.\n\nSecurity code (you can ignore this): ${req.session.nonce}`)
}

然后,不是硬编码要签名的消息,而是通过 AJAX 从服务端检索它:

代码语言:javascript
复制
import axios from 'axios'
import { ethers } from 'ethers'

// On production, you should use something like web3Modal
// to support additional wallet providers, like WalletConnect
const web3 = new ethers.providers.Web3Provider(window.ethereum)

const message = await axios.get('/api/auth/nonce').then(res => res.data)

await axios.post('/api/auth/login', {
 address: await web3.getSigner().getAddress(),
 signature: await web3.getSigner().signMessage(message),
})

最后,在检查签名之前,我们需要从会话中将 nonce 提取出来,从而重建消息。

工具包

有一些软件包可以处理这些事情。我建议在 Node 上用passport-web3[5],如果你正在用 PHP 和 Laravel ,我建议用 and laravel-web3-login[6]。如果你发现其他语言包,请私信我[7]

原文链接:https://m1guelpf.blog/VBlaOTPAxQNFHjl5n-C3BvhkpizvAui9A4RJDwFiQ3k

参考资料

[1]

影无双: https://learnblockchain.cn/people/58

[2]

Showtime: https://tryshowtime.com

[3]

Foundation: https://foundation.app

[4]

我的签名验证的 PHP 实现: https://github.com/m1guelpf/laravel-web3-login/blob/

[5]

passport-web3: https://github.com/coopermaruyama/passport-web3

[6]

laravel-web3-login: https://github.com/m1guelpf/laravel-web3-login

[7]

私信我: https://twitter.com/m1guelpf

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-03-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 深入浅出区块链技术 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 从钱包到服务端
  • 在服务端验证签名
  • 防止签名被利用
  • 工具包
    • 参考资料
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档