はじめに

『DApps開発入門』という本や色々記事を書いているかるでねです。

今回は、異なるアプリやドメインでもユーザーの同一アカウントを正しく特定できる仕組みを提案しているERC7555についてまとめていきます!

以下にまとめられているものを翻訳・要約・補足しながらまとめていきます。

他にも様々なEIP・BIP・SLIP・CAIP・ENSIP・RFC・ACPについてまとめています。

概要

ERC7555は、アプリケーションがユーザーのアカウントを見つけるための共通インターフェースと、その振る舞いを標準化する提案をしています。 ここでいうアカウントには、一般的な外部所有アカウント(EOA:秘密鍵で直接操作するふつうのウォレットアドレス)だけでなく、スマートコントラクトとして実装されたスマートアカウントも含まれます。

特に、通常のEthereumの楕円曲線 secp256k1 を使わない署名鍵で構成・デプロイされたアカウントも対象にしています。 例えば、パスキー(ブラウザやOSに組み込まれた FIDO ベースの認証)、YubiKey(ハードウェアセキュリティキー)、スマートフォンのセキュアエンクレーブ(iOS/Android の安全な専用領域)などです。

ERC7555の目的は、アプリケーションやドメイン(WebサイトのURLごとの世界)をまたいで、「ユーザーのアドレスをどう取得するか」を統一することです。 どのアプリでも同じやり方でユーザーのアドレス群にアクセスできるようにし、アプリごとにバラバラな仕組みになるのを避けます。

動機

アカウント抽象化の進展と複雑さの増加

ERC4337によるアカウント抽象化が進んだことで、ウォレットの柔軟性は大きく向上しました。

ERC4337については以下の記事を参考にしてください。

例えば、以下のようなことが可能になっています。

  • マルチシグトランザクション 複数人の署名が揃わないと送金できない仕組み。
  • ソーシャルリカバリー 友人や別デバイスの承認でウォレットを復元できる仕組み。
  • コントラクトやアカウントのホワイトリスト 特定の相手にだけ送金やコールを許可する仕組み。
  • セッションキー 一定期間・特定の操作だけを許可する一時的な鍵。

こうした柔軟性の一方で、「アカウントがバラバラに増えてしまう」という新しい複雑さが生まれています。 ユーザーが自分名義のアカウントをすべて把握できず、アプリ側もそれらを正しく見つけられないためです。 ERC7555は、このアカウント断片化の問題を減らすための解決策として提案されています。

アカウント断片化とは何か

アカウント断片化とは、1人のユーザーに紐づくアドレスが、EOA・スマートアカウントの両方でバラバラに存在し、それぞれが互いに意識されていない状態を指します。

例えば、ユーザーがあるアプリで作ったスマートアカウントを、別のアプリが認識できず「新規ユーザー」として扱ってしまうケースです。 結果として、ユーザーは複数のアカウントを持つことになり、資産がそれぞれに分散してしまいます。 どのアプリも、ユーザーがすでにどんなアカウントを持っているかを共通の方法で知ることができないのが原因です。

ERC4337登場前後での署名方式の違い

ERC4337以前

ERC4337が登場する前は、スマートコントラクトアカウントとやりとりするには、secp256k1 という楕円曲線暗号に基づく鍵ペアからの署名が必要でした。 これは「通常のEthereumウォレット(MetaMask やハードウェアウォレットなど)で使う秘密鍵」と同じ種類の鍵です。

この前提があったため、「1つの秘密鍵=1つの EOA=それに紐づくスマートアカウント」という関係が比較的一貫していました。

ERC4337 以降

ERC4337の導入以降は、次のような「secp256k1 ではない署名方式」が広く使えるようになりました。

  • パスキー ブラウザやOSが提供する FIDO ベースの鍵で、Webサイトにログインするときに使うもの。
  • YubiKey USBやNFCで接続するハードウェアセキュリティキー。
  • iOS / Android のセキュアエンクレーブ スマホ内の安全な領域で鍵を生成・保管し、外に出さない仕組み。

これらはEthereum標準の secp256k1 鍵とは異なる曲線や方式を使うことが多く、そのままではEthereumのトランザクションに署名できません。 そのため、ERC4337の世界では、ペイマスター(ユーザーの代わりにトランザクションを送ってくれる仕組み)に依頼して、ユーザーの署名を検証しつつトランザクションをネットワークへ投げてもらう、というパターンが一般的になります。

さらに、多くのウォレットプロバイダやサービスは、鍵生成の際に追加の仕組みを組み合わせています。

  • Shamir秘密分散 秘密鍵を複数の断片に分け、一定数を集めると復元できる仕組み。
  • MPC(Multi-Party Computation) 複数のサーバやデバイスが協調して秘密鍵相当の計算を行い、どこにもフルの秘密鍵を置かない方式。
  • セキュアエンクレーブ連携 秘密鍵をデバイス内の安全領域だけで扱い、アプリ側は直接鍵を見られない方式。

こうした多様な実装がある結果、「あるユーザーが複数のアプリを使ったときに、同じEOAや同じスマートアカウントのアドレスを再現するための統一的な方法」が存在しなくなっています。

ドメインごとに異なる鍵が作られる問題

パスキーやYubiKeyなどのセキュリティデバイスは、多くの場合「ドメインごとに異なる鍵ペア」を自動生成します。 例えば、app-a.example.com と app-b.example.com は別のWebサイトと見なされ、それぞれに別の鍵が紐づきます。

この性質により、以下のような状況が起こります。

  • モバイルアプリ A(App A)とWeb アプリ B(App B)の2つのアプリがある
  • 両方のアプリがパスキーを使った認証を実装している
  • ユーザーがそれぞれのアプリでログインすると、App A と App B は異なる鍵ペアを取得する

この場合、ユーザー本人は「どのアプリでも自分は自分」と思っていますが、アプリ側から見ると「App Aのユーザー」と「App Bのユーザー」は別の鍵を持った別人のように見えます。

ユーザーの感覚としては、ハードウェアウォレットや一般的なウォレットアプリのように、「どこで使っても同じアドレス」が期待されます。 しかし実際には、セキュリティデバイスがドメイン単位で鍵を分けているため、それが実現できていません。

ERC-4337による問題の増幅

ERC4337を活用するアプリは、ユーザーの鍵管理を抽象化し、オンボーディング(初回利用時のセットアップ)を簡単にしようとします。 例えば、「メールアドレスやパスキーだけでウォレットを用意できる」ような体験です。

しかし、現在の状態では、アプリ側が「そのユーザーがすでにどこかでERC4337対応のスマートアカウントをデプロイしているかどうか」を知る共通の仕組みがありません。 そのため、以下のようなことが起きます。

  1. ユーザーがあるサービスでERC4337スマートアカウントを作成・利用している。
  2. その後、別のアプリがERC4337対応のオンボーディングを提供する。
  3. しかし、そのアプリはユーザーの既存スマートアカウントを検出できない。
  4. 結果として、新しいスマートアカウントを、そのアプリのドメインに紐づく形で新規デプロイしてしまう。

こうして、ユーザーはアプリごとに別々のスマートアカウントを持つことになり、断片化がさらに進みます。

アカウント断片化がユーザーとアプリに与える影響

既存のアカウントを正しく特定・共有できない状態が続くと、次のような影響が出ます。

  • アプリはユーザーを「新規ユーザー」と誤認し、それぞれに新しいアカウントを用意してしまう。
  • ユーザーは自分でも気づかないうちに、多数のアカウントを持つことになる。
  • 資産(トークンやNFTなど)は複数のアカウントに散らばり、どこに何があるのか把握しづらくなる。
  • アプリ同士がユーザーを同一人物として扱えないため、シングルサインオン(1回のログインで複数サービスをまたいで認証する仕組み)も実現しにくい。

ERC7555は、このような断片化を抑え、ユーザーとアプリが一貫したアカウント情報を共有できるようにすることを目指しています。

ERC7555が目指すこと

提案されている標準が「何を解決したいのか」は、以下のように整理できます。

目的 説明
アプリがユーザーの署名用アドレスを標準的な方法で取得できるようにする アプリケーションが「このユーザーはどのアドレスでトランザクションに署名するのか」を、共通のインターフェースを通じて問い合わせられるようにします。これにより、アプリごとに異なる独自実装に頼らず、同じ方法で署名アドレスを取得できます。
代替的な署名方式向けのシングルサインオン(SSO)を標準化する パスキー、YubiKey、セキュアエンクレーブなど、secp256k1 以外の鍵方式でも、複数のアプリ間で共通のサインイン体験を提供できるようにします。ユーザーは同じ自分を、複数サービスで再利用できるようになります。
アプリが自分のサービス経由で作成したスマートアカウントを開示する方法を標準化する あるアプリやオンボーディングサービスがユーザーのためにデプロイしたスマートアカウントを、他のアプリにも分かる形で開示できるようにします。他のアプリはそれを利用して、ユーザーの既存アカウントを特定できます。

ERC7555の対象外となる範囲

一方で、ERC7555は「何でも決める総合ルール」ではなく、あえてスコープ外としている領域も明確にしています。

対象外の項目 説明
ドメインをまたいでメッセージに署名する方法 あるドメインで作られた鍵やアカウントを、別ドメインでどう使い回すか、あるいはクロスドメインの署名方式をどうするか、といった詳細な仕組みは定義しません。
プロバイダがユーザーの鍵ペアをどう生成するか ウォレットや認証プロバイダが、どのような方法(Shamir 秘密分散、MPC、セキュアエンクレーブなど)で鍵を作り、保管するかは、各プロバイダの設計に任されています。ERC はその内部実装には踏み込みません。
アプリケーションのUIロジック 実際にユーザーにどのような画面やフローを見せるか、どのボタンを押させるか、といったユーザーインターフェースの設計は、この標準の対象外です。アプリ開発者は自由に UX を設計できます。

仕様

定義

用語 説明
Smart account ERC4337に準拠したスマートコントラクトアカウントです。モジュラーな設計になっていて、署名検証やポリシーなどを差し替え可能な構造を想定しています。
Domain サーバーやウェブサイトを識別する文字列です。例として ethereum.org や ABCDE12345.com.example.app のような文字列を指します。ブラウザでアクセスするときの「ドメイン名」と同じイメージです。
EOA Externally Owned Account の略です。単一の秘密鍵で制御される、従来型のEthereumアカウントを指します。一般的なウォレットアドレスがこれに当たります。
Provider ユーザーを認証し、そのユーザー向けの鍵ペア(公開鍵と秘密鍵の組み合わせ)を生成できる第三者サービスです。ウォレットサービスや認証サービスが Provider になります。

ここで出てくる「鍵ペア」は、暗号学的な公開鍵・秘密鍵のペアを指します。 ユーザーがトランザクションやメッセージに署名するための元となる情報です。

リダイレクトの仕組み

ERC7555は、アプリケーションがユーザーを認証する時に、「リダイレクト」という仕組みを使います。

アプリケーションは、URIリクエスト構文に従って、特定のProviderが用意したURI(Web アドレス)へユーザーを遷移させます。 ここでいうURIは、「https://example.com/auth?...」のような形式の識別子です。

アプリケーション側は、ユーザー認証が終わったあとに戻ってきてもらうための「リダイレクトURI」を実装しなければなりません。 このリダイレクトURIが正しく実装されていないと、Providerからのコールバック(認証結果の返却)を受け取れず、正常にレスポンス(スマートアカウントなどの情報)を受信できません。

要するに、アプリは以下の2つを行う必要があります。

  • Providerが指定するURIに、必要なクエリパラメータをつけてユーザーを移動させる。
  • Providerからの戻り先となる redirect_uri を事前に用意し、そのURIでレスポンスを受け取る。

利用可能なルート

ERC7555は、Providerが最低限サポートするべきルート(エンドポイント)を2つ定義しています。

ルート 役割
/auth/ ユーザーを認証し、スマートアカウントに関する情報(資格情報)を取得するためのルートです。
/sendTransaction/ ユーザーにトランザクションを署名させるためのルートです。認証と「プラグイン登録」(アプリ側の拡張的な登録処理)を、1回のリダイレクトでまとめて行えるようにするための便利なルートと位置づけられています。

/sendTransaction/ は、別々に /auth/ とトランザクション送信の処理を行う代わりに、「認証+トランザクション署名」をまとめて行いたい場合に使う想定になっています。

スキーマ

ERC7555は、スマートアカウントのアドレスを返すときの形式も指定しています。

smart_account_address は、CAIP10形式で返す必要があります。

CAIP10については以下の記事を参考にしてください。

CAIP10は、ブロックチェーンアドレスをチェーン情報とセットで表現するための標準フォーマットです。 例えば「チェーン ID + アドレス」を eip155:1:0x1234... のように1つの文字列として表すルールです。 これにより、「どのチェーン上のどのアドレスか」を一意に扱いやすくなります。

Authルート

Authルートは、ユーザーを認証し、そのユーザーに紐づくスマートアカウントアドレスを取得するためのルートです。

リクエストスキーマ

text
parameters:
  - in: query
    name: redirect_uri
    schema:
      type: string
    description: The uri that the provider should redirect back to.

  - in: query
    name: chain_id
    schema:
      type: string
    description: The chain_id of a given network.

ここで使われているパラメータは以下のとおりです。

パラメータ 説明
redirect_uri 認証処理が終わったあとで、Provider がユーザーを戻す先の URI です。アプリ側が用意したコールバック用のエンドポイントになります。
chain_id 対象とするネットワークのチェーン ID を表す文字列です。Ethereum Mainnet であれば「1」、別チェーンやテストネットであればそのチェーンのIDを指定します。

ここでいうチェーンID は、ブロックチェーンネットワークを一意に識別する数値を文字列として渡すものです。

レスポンススキーマ

Authルートのレスポンスも、クエリパラメータとして定義されています。

text
parameters:
  - in: query
    name: smart_account_address
    schema:
      type: string
    description: The on-chain address for a given smart account, formatted using CAIP-10

レスポンスでは、以下の情報が返されます。

パラメータ 説明
smart_account_address 対象のスマートアカウントのオンチェーンアドレスです。形式はCAIP10に従います。チェーンIDとアドレスが組み合わさった文字列になります。

リクエスト構文

AuthルートへのリクエストURI は、以下のような構文で表現されます。

text
https://<PROVIDER_DOMAIN>/auth/?
    redirect_uri=<YOUR_REDIRECT_URI>
    &chain_id=<CHAIN_ID>

<PROVIDER_DOMAIN> にはProviderのベースURLが入り、redirect_uri と chain_id をクエリパラメータとして付与します。 アプリはこのURLにユーザーをリダイレクトすることで、Provider側の認証画面などに遷移させます。

レスポンス構文

認証が完了すると、Providerはアプリ側の redirect_uri に対して、以下のような形式でリダイレクトを返します。

text
https://<YOUR_REDIRECT_URI>/auth/?
    smart_account_address=<SMART_ACCOUNT_ADDRESS>

アプリは、自身の redirect_uri に付与された smart_account_address クエリパラメータを読み取ることで、「認証されたユーザーに対応するスマートアカウントアドレス(CAIP10形式)」を取得します。

sendTransaction ルート

sendTransaction ルートは、ユーザーのスマートアカウントでトランザクションを署名・送信する時に利用するルートです。 同時に、Authルートと同様に、スマートアカウントアドレスも返します。

リクエストスキーマ

仕様では、以下のように記述されています。

text
parameters:
  - in: query
    name: redirect_uri
    schema:
      type: string
    description: The uri that the provider should redirect back to.

  - in: query
    name: chain_id
    schema:
      type: string
    description: The chain_id of a given network.

  - in: query
    name: transaction
    schema:
      type: string
    description: The RLP encoded transaction that needs to be signed

各パラメータの意味は以下です。

パラメータ 説明
redirect_uri トランザクション処理が終わったあと、Provider が戻ってくる先の URI です。
chain_id トランザクションを送信するチェーンのIDを示す文字列です。
transaction 署名対象のトランザクションを、RLPエンコードした文字列です。RLP(Recursive Length Prefix)は、Ethereumでトランザクションなどのデータをバイト列に変換するための標準的なエンコード方式です。

アプリは、自分の側でトランザクションの内容を組み立て、それをRLP形式にエンコードして transaction パラメータに載せて渡します。

レスポンススキーマ

sendTransaction ルートのレスポンスは、スマートアカウントアドレスとトランザクションハッシュを含みます。

text
parameters:
  - in: query
    name: smart_account_address
    schema:
      type: string
    description: The on-chain address for a given smart account, formatted using CAIP-10

  - in: query
    name: tx_hash
    schema:
      type: string
    description: The hash of the transaction

返される情報は以下です。

パラメータ 説明
smart_account_address トランザクションを実行したスマートアカウントのアドレスです。CAIP10形式で返されます。
tx_hash 実際にネットワークへ送信されたトランザクションのハッシュ値です。ブロックエクスプローラでトランザクションを追跡したり、ステータスを確認するために使われます。

リクエスト構文

sendTransaction ルートへのリクエストURI は、以下のようになります。

text
https://<PROVIDER_DOMAIN>/sendTransaction/?
    redirect_uri=<YOUR_REDIRECT_URI>
    &chain_id=<CHAIN_ID>
    &transaction=<RLP_ENCODED_TRANSACTION>

アプリは、RLPエンコード済みのトランザクションを transaction に詰め、redirect_uri と chain_id とともに Provider へ渡します。

レスポンス構文

トランザクション処理が終わると、Providerは以下のような形式でアプリの redirect_uri にリダイレクトします。

text
https://<YOUR_REDIRECT_URI>/sendTransaction/?
    smart_account_address=<SMART_ACCOUNT_ADDRESS>
    &tx_hash=<TX_HASH>

アプリは、このリダイレクト先のクエリパラメータから、どのスマートアカウントを使ってトランザクションが送信されたか(smart_account_address)、そしてそのトランザクションのハッシュ(tx_hash)を取得できます。

補足

リダイレクト方式を採用する理由

ERC7555は、現在のWebで一般的に使われている SSO(シングルサインオン)の仕組みを参考にしています。 GoogleログインやOAuthのような仕組みでは、クライアントアプリがユーザーを認証サービスへリダイレクトし、認証後に元のアプリへ戻ってくる、という単純なリクエスト/レスポンスの流れになっています。

ERC7555も同じ考え方で、アプリケーションとプロバイダの間を

  • アプリ → プロバイダへのリダイレクト(認証や署名の要求)
  • プロバイダ → アプリへのリダイレクト(結果の返却)

という2ステップにまとめています。 これにより、HTTPセッションやサーバー側の状態を複雑に持たなくても、ブラウザベースでシンプルにやりとりできるようにしています。

アプリケーション側の振る舞い

最初のリクエスト

アプリケーションは、ユーザーを認証したいとき、指定されたProviderに対してユーザーをリダイレクトします。 このとき、アプリがProviderに渡す情報は基本的に「コールバック用の URL(redirect_uri)」だけです。

アプリが余計な状態をProviderに持たせず、Provider側のWebサイトが「ステートレス(サーバー側にセッション状態を持たない構成)」で動作できるようにする意図があります。 Provider側は、渡された redirect_uri だけを頼りに、以下のシンプルな役割に集中できます。

  • 認証処理を行う
  • 処理結果を redirect_uri に載せて返す
Providerからのレスポンス処理

ユーザーがProviderから元のアプリケーションへリダイレクトされると、アプリはURLのクエリパラメータをパースして、必要な情報を取り出します。

アプリが受け取る情報は以下のように整理されています。

  • 署名者アドレス(signer address)
  • 対応するスマートアカウントアドレス

アプリはこれらを元に、「どのアドレスをユーザーの代表アドレスとして使うか」、「どのスマートアカウントに紐づけて操作するか」を判断できるようになります。

プロバイダ側の振る舞い

ユーザーがProviderのサイト(例えば https://eth-sso.ethereum.org/auth)に遷移すると、ProviderはまずリダイレクトURLを解析します。 ここでいうリダイレクトURLとは、アプリケーションから渡された redirect_uri を含むリクエストURLです。

Providerはこの情報を元に、ユーザー認証を行います。 認証方法は仕様として固定されておらず、「有効な公開アドレスを生成でき、かつ Providerを通じてデプロイされたスマートアカウントを復元できること」が満たされていれば問題ありません。

例えば、パスキーやハードウェアキー、MPC ウォレットなど、どのような手段でも構いません。 重要なのは、最終的に以下の2つを得られることです。

  • ユーザーを表す公開アドレス(EOA 相当のアドレスなど)
  • そのアドレス経由でデプロイされたスマートアカウント(あれば)

これを元に、Providerはアプリ側の redirect_uri に対して結果を返します。

参考実装

location.replace と location.href の使い分け

ERC7555は、クライアント側でリダイレクトするときの window.location.replace() と window.location.href のどちらを使うべきかまでは決めていません。 どちらを使うかは、アプリケーションがユーザー体験をどう設計するかに任されています。

  • location.replace() は、現在のページの履歴を置き換えるため、ブラウザの戻るボタンで1つ前に戻れない挙動になります。
  • location.href は、新しい履歴として追加されるため、戻るボタンで前のページに戻ることができます。

例えば、「SSOの途中画面に戻られても意味がない」と判断するなら location.replace() を使う、というように選択できます。

サンプルURIリクエスト

仕様に示されているサンプルリクエストは以下のとおりです。

text
https://eth-sso.ethereum.org/auth?redirect_uri=http://myapp.com/eth-sso/callback/&chain_id=1

この例では、以下のような意味になります。

要素 説明
https://eth-sso.ethereum.org/auth Providerが提供する /auth ルートです。
redirect_uri=http://myapp.com/eth-sso/callback/ 認証後に戻ってくるコールバック先を指定しています。
chain_id=1 Ethereum Mainnet(チェーン ID 1)であることを表しています。

アプリケーションは、自分のドメイン(ここでは myapp.com)配下に eth-sso/callback/ というパスを用意し、そこがレスポンスを受け取る窓口になります。

サンプルレスポンス

Providerからアプリへのレスポンス例は次のとおりです。

text
http://myapp.com/callback/?smart_account_address=0xb...c

ここでは、smart_account_address というクエリパラメータに、ユーザーに紐づくスマートアカウントアドレスが載っています。 アプリはこのアドレスを取得して、その後の処理(残高表示、トランザクション送信など)に利用します。

アプリ側のロジック例

仕様では、アプリケーション側のJavaScriptのイメージコードが示されています。

javascript
// https://myapp.com
// User triggered authentication function
function auth() {
    window.location.replace("https://eth-sso.ethereum.org/auth?redirect_uri=myapp.com&chain_id=1/eth-sso/callback/");
};

// App level routing logic (generic router)
route("/eth-sso/callback/", function() {
    let params = (new URL(document.location)).searchParams;
    let smartAccountAddress = params.get("smart_account_address");
});

概要は以下です。

部分 説明
auth 関数 ユーザーが「ログイン」などのボタンを押したときに呼ばれる関数です。window.location.replace() を使ってProviderの /auth ルートにリダイレクトしています。
route("/eth-sso/callback/", ...) アプリ内のルーティング処理を行う部分です。/eth-sso/callback/ にアクセスされたときにコールバック関数を実行し、URL オブジェクトを使ってクエリパラメータを解析しています。
params.get("smart_account_address") クエリパラメータから smart_account_address を取り出し、スマートアカウントアドレスとして利用します。

このロジックにより、アプリは「ログイン処理の開始」と「ログイン結果の取得」をそれぞれ自分の側で実装できます。

プロバイダ側のロジック例

Provider側のイメージコードも示されています。

javascript
// eg: https://eth-sso.ethereum.org/auth
route("/eth-sso/callback/", function("/auth") {
    let params = (new URL(document.location)).searchParams;
    let redirectUrl = params.get("redirect_uri");
    // Authenticate the user (eg: with passkeys)
    let address = "...";
    // Get smart account if available
    let smartAccountAddress = getSmartAccount(address);
    window.location.replace(`http://${redirectUrl}/?smart_account_address=${smartAccountAddress}`);
});

この例の流れは以下のようになります。

ステップ 説明
1 Providerのルーティングで、/auth に相当するパスへのアクセスを受け取ります。
2 document.location からクエリパラメータを取得し、redirect_uri を取り出します。ここにアプリ側のコールバック URL が入っています。
3 ユーザー認証を行います。コメントには「passkeys(パスキー)」の例が書かれており、パスキーでユーザーを認証してアドレスを得る想定です。
4 認証で得たアドレスを元に getSmartAccount(address) を呼び出し、ユーザーに紐づくスマートアカウントアドレスを取得します(存在する場合)。
5 取得した smartAccountAddress をクエリパラメータに載せて、redirectUrl に対して window.location.replace でリダイレクトします。

これにより、Provider は「認証」と「スマートアカウントのリカバリー」を済ませた状態で、アプリ側に情報を返すことができます。

セキュリティ

アドレスなりすましの懸念と対策

仕様では、「ユーザーが別人のアドレスを偽装(spoof)できるのではないか」という懸念について触れています。 具体的には、Providerを経由せずに、悪意のあるユーザーが直接 redirect_uri を叩き、任意の smart_account_address を付与してアプリケーションに戻るという想定です。

例えば、以下のような形で、自分で勝手にURLを構築してアクセスするイメージです。

text
http://myapp.com/eth-sso/callback/?smart_account_address=任意のアドレス

アプリ側が「smart_account_address が入っていれば正しいProviderからのレスポンスだ」と信じてしまう実装になっていると、他人のアドレスを自分のもののように扱わせることが可能になります。

この問題への対策として、仕様では「ユーザーに challenge メッセージへ署名させる」方法が挙げられています。 ここで言う challenge メッセージとは、「一度きりのランダムな文言」や「時刻・クライアント情報などを含んだテキスト」で、ユーザーに署名してもらうことで本人性を確認するためのメッセージです。

また、その具体例として「SIWE(Sign-In With Ethereum)」の活用が挙げられています。 SIWEは、Ethereumの署名機能を使ってWebログインを行うためのプロトコルで、ユーザーに

  • ドメイン
  • アドレス
  • 有効期限
  • nonce(ランダムな一意の値)

などを含んだメッセージに署名してもらい、その署名をサーバー側で検証することで「本当にそのアドレスの秘密鍵を持っている本人か」を確かめます。

ERC7555の文脈では、こうした署名ベースの検証を組み合わせることで、以下の状態を作れると説明しています。

  • Providerを飛ばして直接 redirect_uri を叩いても通らない
  • 本物のユーザーだけが正しいアドレスを名乗れる

リダイレクトURIにワイルドカードを認めない理由

仕様では、リダイレクトURIにワイルドカード(例えば https://myapp.com/* のようなパターンマッチ)を認めていません。 これは「ネストしたオープンリダイレクト脆弱性」を防ぐためです。

オープンリダイレクト脆弱性とは、「あるサイトのURLに細工することで、別の任意のサイトにリダイレクトさせられる」問題です。 攻撃者はこれを利用して、以下の攻撃を行うことができます。

  • 一見正しいドメイン(信頼できるサービス)へのリンクに見せかける
  • そこからさらにフィッシングサイトなどの悪意あるページに飛ばす

もしリダイレクトURIにワイルドカードを許すと、以下のような流れが可能になります。

  1. Provider側から見ると https://myapp.com/* のようなURIなら何でも許可される。
  2. 攻撃者が https://myapp.com/redirect?target=悪意あるサイト のようなパスを利用し、myapp.com 内部のオープンリダイレクト機能を経由してフィッシングサイトへ誘導する。
  3. Providerと myapp.com 自体は正しく見えるが、最終的にはユーザーが危険なサイトに飛ばされる。

このように、「ワイルドカードで許可された範囲の中に、さらにオープンリダイレクトの入り口がある」と、リダイレクトが階層的に連鎖してしまい、攻撃がやりやすくなります。

ERC7555は、これを防ぐために、リダイレクトURIにワイルドカードを使わない設計としています。 具体的には、「正確なパスのみを許可する」、「あらかじめ決めた少数のURIだけを固定で許可する」といった運用を想定しています。

引用

Alexander Müller (@alexmmueller), Gregory Markou (@GregTheGreek), Willem Olding (@Wollum), Belma Gutlic (@morrigan), Marin Petrunić (@mpetrunic), Pedro Gomes (@pedrouid), "ERC-7555: Single Sign-On for Account Discovery [DRAFT]," Ethereum Improvement Proposals, no. 7555, November 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7555.

最後に

今回は「異なるアプリやドメインでもユーザーの同一アカウントを正しく特定できる仕組みを提案しているERC7555」についてまとめてきました! いかがだったでしょうか?

質問などがある方は以下のTwitterのDMなどからお気軽に質問してください!

他の媒体でも情報発信しているのでぜひ他も見ていってください!