MetaMaskから取得したアドレスとWebサービス上のユーザーを紐付ける
Webサービス上でユーザーにethereumのウォレットアドレスを登録してもらい、そのアドレスを使ってあれこれやりたいことがあったとする。
そのとき、ユーザーが入力したアドレスが本当に本人が所有しているものなのかは何らかの検証が必要になる。例えばweb3.jsを使ってMetaMaskとの接続をリクエストして、ユーザーが承認すればアドレスが取得できるが、それをバックエンドに送信する際に改ざんできてしまう。
ということで、アドレスの認証にはどういう方法があるの?ってのを調べたのでメモ。
結論から書くと、ECDSA署名を検証することでアドレスの検証を行うecrecoverという方法があるみたいだった。
いくつかわかりやすい解説記事があった。
- 【DApps】MetaMaskを使ったパスワード不要のユーザ認証(React+Node.js+MongoDB) | ALIS
- DApps のユーザー認証に web3.eth.personal.sign を使おう! - Tech Inside Drecom
- MetaMask Tutorial: One-click Login With Blockchain Made Easy | Toptal
記事を読む限りではそんなに難しくはない感じ(暗号学的に理解してるとは言ってない)。詳しくは上の記事を読んでもらうとして、じゃあ例えばRubyでどうやったらいいの〜を試した。雑コードだけど流れとしてはこんな感じになる。
require "eth" # https://github.com/q9f/eth.rb/
# 1.ユーザーがあらかじめ所有している秘密鍵(とそこから作られたアドレス)がある
key = Eth::Key.new
private_key = key.private_hex
public_key = key.public_hex
address = key.address
# 2.ユーザーがWebサービス上でアドレスを入力する
puts "address = #{address}"
# 3.ユーザーに紐付けたnonceを取得して署名を要求
nonce = rand(100000)
message = "あなた様が入力したアドレスを本当に所有してるか検証したいのでこれに署名してくれ〜: #{nonce}"
# 4.ユーザーがMetaMaskで署名(という想定)
signature = key.personal_sign message
# 5.サーバーサイドでは送られてきた署名からアドレスの所有者であることを確認する
recovered_key = Eth::Signature.personal_recover message, signature
recovered_address = Eth::Util.public_key_to_address recovered_key
if Eth::Signature.verify message, signature, address
puts "verified"
end
q9f/eth.rbというめちゃ便利gemがあったのですんなり実現できてしまった。とはいえgemのコードを読んだわけじゃないからこれで本当にecrecoverが実現できてるのかまでは確認してない。それとこの方法で本当にWebサービス上のユーザーと所有するアドレスを”安全に”紐づけたと言えるのはいまいち自信がない。少なくとも「誰かテキトーな人のアドレスを入力しちゃおう」みたいなことはできないんじゃないかとは思ってるけど…。