🐯

HTML + JS + Rails フロント署名 → バックで署名の検証 [初心者向け]

2022/03/15に公開

metamaskなどのウォレットアドレスをバックエンド側で利用したい時、フロントから送られてくる値が本当に正しいわからないと思います。
そこで、署名を利用することで、正しいウォレットアドレスを取得できるので、その方法を解説します。

フロントで署名 → バックエンドで署名の検証を行う事で、正しいアドレスという事が証明できます。

今回はHTML + Javascript + Rails で行います。

HTML

まずはフロント側の HTML になります。
ライブラリを読み込んでいるのと、js を実行するボタンが付いてるだけとなりますー!

axios を使っていますが、別に他のものでも良いかと。
ブロックチェーンのライブラリに関しては、web3.js を使っています。
ethers.js 版も、また今度上げようかなと思っていますー!

index.rb
<html>

<head>
  <meta charset="UTF-8" />
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
  <script src="https://cdn.jsdelivr.net/gh/ethereum/web3.js@1.7.0/dist/web3.min.js"></script>
</head>

<body>
  <button class="enableEthereumButton">Enable Ethereum</button>

  <script src="./register.js">
  </script>
</body>

</html>

Javascript

つづいて、js のファイルになります。

register.js
const registerUserButton = document.querySelector('.registerUserButton');

registerUserButton.addEventListener('click', () => {
  unlockWallet();
});

async function unlockWallet() {
  //web3がない、もしくはメタマスクのない状況(window.ethereumがない状況)ならその注意文を出してreturn
  if (typeof web3 == 'undefined' && typeof window.ethereum == 'undefined') {
    console.log('Wallet is not installed!');
    return
  }

  // web3をnewする metamaskであろうがその他であろうが、同じバージョンのweb3を使う(ethers.jsでもいいかも)
  window.web3 = new Web3(window.ethereum);

  ethereum.request({ method: 'eth_requestAccounts' })
  .then(function(result){
    signAndRegister(result[0]);
  })
}

function signAndRegister(wallet_address){
  web3.eth.personal.sign("Agree", wallet_address)
  .then(function(result){
    const params = {
      signed_data: result,
      address: wallet_address,
    }
    axios.post(apiUrl, params)
    .then(function(res) {
      console.log(res)
    })
    .catch(function(error) {
      console.log(error)
    })
  })
}

↓ こちらのメソッドは metamask と接続したウォレットのアドレスを返してくれます。

ethereum.request({ method: "eth_requestAccounts" });

https://docs.metamask.io/guide/ethereum-provider.html

↓ こちらは実行されると metamask のポップアップが出てきて署名を行えます。

web3.eth.personal.sign("Agree", wallet_address);

このメソッドからの返り値である署名データをバックエンドに渡してあげます。

https://docs.metamask.io/guide/signing-data.html#signing-data-with-metamask

Rails

次は Rails で、署名の検証を行います。
フロントから送られてきた署名データからアドレスを復元して、一緒に送られてきたアドレスと一致しているか確認をします。

ruby-eth の Gem を使用する事で署名からアドレスを復元できます。

gem 'eth', '~> 0.4.10'
signatures_controller.rb
class Api::V1::SignaturesController < Api::ApplicationController
   def create
    address = recover_address(params[:signed_data], "Agree")

    #復元したアドレスと、投げられてきたアドレスが異なる場合はErrorを返す(不正)
    if address.downcase != params[:address].downcase
      @error_message = "Error"
      return
    end

    #ここで何かしら行いたい動作をする。
  end

  private
    #署名からpublic keyを復元し、そのpublic_keyからaddressを復元する
    def recover_address(signature, message)
      public_key = Eth::Key.personal_recover(message, signature)
      address = Eth::Utils.public_key_to_address(public_key)
      return address
    end
end

署名データと署名メッセージから公開鍵を復元し、そこからアドレスを出してきます。

public_key = Eth::Key.personal_recover(message, signature)
address = Eth::Utils.public_key_to_address(public_key)

その後、一緒に送られてきたアドレスと復元したアドレスが一致していれば、署名の検証完了となります!!
これで正しいウォレットアドレスがバックエンドで利用する事ができます。

まとめ

以上、metamaskなどのウォレットアドレスをバックエンド側で利用したい時の検証方法の解説となります。

Discussion