Web3 基盤言語(二):Move はどのようにフラッシュローン再入攻撃を回避するのか?
著者:宋嘉吉 任鹤义、国盛証券研究所
前回の報告では、基礎的なプログラミング言語の特徴から、 MoveとSolidity(イーサリアム)の利点と特徴を比較しました。Web3の基礎研究として、本稿では、最も特徴的なアプリケーションであるフラッシュローンの観点から、イーサリアムとMoveがそれぞれどのようにフラッシュローンを実現し、Moveがどのようにフラッシュローン攻撃を回避しているのかを分析します。
イーサリアムの契約間の相互作用は、メッセージの相互通信によって実現される状態の一貫性を持ち、再入可能性や動的呼び出しを許可します。この特徴は、フラッシュローンを実現するための基礎を提供します。この間、契約間の関数は互いに呼び出し合うことができ、呼び出しの過程で制御権が移転します。もしDeFiプロジェクトのプラットフォーム契約に脆弱性がある場合、アービトラージャーはその契約の悪意のあるコードを利用して、対応する関数を呼び出し資産を盗むことができます------契約間の状態同期情報の差を利用して、プロセスが終了する前に二重支払いを行ったり(イーサリアムの資産は単に値を代入するだけ)、あるいは許可されていないロジックを繰り返し実行して盗むことができます。
再入攻撃の前提は、攻撃者が展開した契約に悪意のあるコードが存在することですが、最も核心的な要因は次の通りです:
1)イーサリアム契約呼び出し時に制御権が移転するため、悪意のある契約に主導権を与えます;
2)プロセスが終了する前に再入(再呼び出し)が可能であり、悪意のある契約は脆弱性を利用して関数を繰り返し呼び出し、資産を盗むことができます(例えば、1回の引き出しプロセスが終了する前に何度も引き出す);
3)加えて、イーサリアムのアカウント資産は数値残高の形で存在するため、資産を繰り返し盗む(ダブルスピンドル)の可能性があり、またプロセスが終了する前に悪意のある契約が関連アカウントの資産残高を変更して盗むことができます。
Move はフラッシュローンの新しい実行プロセスを提案しました------ホットポテトモデルで、根本的に再入を放棄します。"ホットポテト(hot-potato)"モデルは、key、store、copy、dropの能力を持たない構造であり、Moveプログラムが取引実行中に一度だけ使用される構造です。drop、key、storeの能力がないため、ホットポテトは"破棄"関数を呼び出すことでプロセスを完結させることしかできません------これはその名の通り、ホットポテトであり、プロセス中のいかなる処理も"熱い手"であり、破棄関数に任せて完結させるしかありません。具体的なプロセスは以下の通りです:
1)フラッシュローンのスマート契約が作動中に"ホットポテト"(hot-potato)のレシート(receipt)を作成します;
2)アービトラージャーがフラッシュローン契約から貸付を受ける際、フラッシュローン契約は貸付資金とホットポテトのレシート(receipt)を送信します;
3)アービトラージャーは貸付資金を利用してアービトラージ操作を行います;
4)アービトラージャーが返済する際、返済関数(repay)を呼び出し、資金とレシート(receipt)を返済関数に送信し、レシートが返済関数に受け取られた後に破棄されます。
フラッシュローンが完了するための前提は、正しい返済とプロセスの終了であり、プロセスが終了する前に悪意のある契約がイーサリアムシステムで再呼び出しを実行し、関連する資産アカウントの値を変更して盗むことができます。Moveシステムのフラッシュローンプロセスが終了するための前提は、正しく資金を返還することに加えて、ホットポテトというリソースを一度限り回収して破棄する処理が必要であり、これがフラッシュローンの原子性を確保します。
一:核心的な見解
フラッシュローンはイーサリアムDeFiエコシステムで最も特徴的なアプリケーションであり、その基礎はイーサリアムが使用するSolidity言語が動的呼び出しと再入を許可していることです。このような動的呼び出しはスマート契約のオープン性、組み合わせ可能性の重要な表れですが、制御権の移転と契約関数の繰り返し呼び出しがもたらすセキュリティリスクは無視できません。業界内ではフラッシュローンを利用した脆弱性のあるDeFi資金プールの攻撃事件が頻発しています。Move言語は動的呼び出しと再入を許可しないため、"ホットポテト"モデルを使用してフラッシュローンをシンプルに実現し、イーサリアムのようなセキュリティ問題を完全に回避できます。 両システムのエコシステムアプリケーションの作業モードには明らかな違いがあり、この違いの根本はMoveの基礎的な言語の特徴にあります。前回の報告『Web3基礎言語:MoveはSolidityのどの不足を補ったのか?』で詳述しました。本稿ではフラッシュローンのアプリケーションの観点から、両者の実装の異なるプレイスタイルを比較します。
二:イーサリアムフラッシュローンの基礎:動的呼び出しと再入
フラッシュローン(Flash Loan)はネイティブなDeFiの新しい製品であり、超高速ローンと理解できます。ユーザーは同一の取引(ブロック)内で借入、アービトラージ、返済を完了し、手数料を支払うだけで済みます。原子取引であるため、借入者は資産を担保にする必要がなく、無担保のアービトラージプランを提供します。
前回の報告『Web3基礎言語:MoveはSolidityのどの不足を補ったのか?』で述べたように:
"モジュール化と契約の組み合わせの観点から、Solidity(イーサリアム)のContract契約はlibrary(静的ライブラリに相当)を通じてメッセージを伝達し、Contract契約間の呼び出しと相互作用を実現します。一方、Move言語はモジュール(module)とスクリプト(script)の設計を使用し、前者はContract契約に類似し、Move言語の契約の組み合わせはモジュール間の組み合わせを通じて、リソース(前述のresources)を伝達します。組み合わせの観点から、SolidityとMoveの違いは非常に明確です。"
イーサリアム契約間の相互作用は、メッセージの相互通信によって実現される状態の一貫性を持ち、再入可能性や動的呼び出しを許可します。つまり、契約間の関数は互いに呼び出し合うことができ、呼び出しの過程で制御権が移転します。この特徴はフラッシュローンを実現するための基礎を提供します。
具体的には、イーサリアムの取引内で、送金操作やその他の一連の契約操作を行うことができ、スマート契約内の機能関数を呼び出して複数の複雑な機能を実行します------つまり、イーサリアムに基づく取引は一連の複雑な取引を統合することが可能です:借入、アービトラージ、返済などの一連の取引操作を統合することができます。
フラッシュローン内のすべての操作は1つのブロック時間内に完了し、現在のイーサリアムのブロック生成速度では、マージ後は約12秒です------核心は12秒ではなく、この一連の取引が最終的に利益を上げ、返済されることです。これが達成されない場合、この取引はブロックにパッケージ化されず、借入者の借入やアービトラージ(失敗)などの操作は有効な取引ではなく、一時的な状態に過ぎません------つまり、原子取引です。したがって、ユーザーはプログラミングを通じて実行する必要のあるすべてのステップを形成し、スマート契約取引を完了させ、借入、使用、返済の3つのステップを実行する必要があります。
上述の複雑な取引操作は、いくつかの契約間の動的呼び出しによって実現され、かつこの呼び出しは再入可能です(つまり、繰り返し呼び出すことができます)。
現在、ユーザーはFURUCOMBOのような第三者プロジェクトを通じて、フラッシュローンを完了するためのより簡易なプラグインプログラミングを行うことができ、実際にコードを書くことなくスマート契約の設計を完成させ、最終的にフラッシュローンに必要な一連の操作を実現します。具体的なアービトラージプロセスは以下の図に示されています(FURUCOMBOプラットフォームを利用し、具体的な為替レートは例示です)。現在、Kyberswapプラットフォームでの価格状況は1 sUSD =0.9927 DAIであり、Uniswapでは1 DAI=1.2411 sUSDです。ユーザーはこれら2つのプラットフォームのDAI-sUSD取引ペアの価格に大きなアービトラージの余地があることを発見し、FURUCOMBOのインターフェースを通じてアービトラージプロセスを設計します。
含まれる内容:1、AAVE借入プラットフォームのフラッシュローン機能から100 DAIを借りる;2、Uniswapを通じて100 DAIを約122のsUSDトークンに交換する;3、Kyberswapプラットフォームを通じてsUSDトークンを約122 DAIトークンに交換する;4、AAVEから借りた100 DAIトークンと手数料0.09 DAIを返済する;5、フラッシュローンを利用したアービトラージプロセス全体が1つのイーサリアム取引内で完了し、約22 DAIの利益を得る。
このイーサリアム取引内で借りた資金が返済されなかった場合、全体の借入取引はブロックにパッケージ化されず、借入は実際には発生しなかったことになります。したがって、借入者の資金は何の影響も受けず------中間の借入とアービトラージのプロセスは一時的な状態であり、マイナーによってパッケージ化され確認されていません。
フラッシュローンの特性と時間的要件に基づき、現在その最も広範な応用はアービトラージ取引です。アービトラージャーは自身の資産を使用することなくアービトラージ操作を行うことができ、フラッシュローンを通じて必要な資金量を得てアービトラージ取引を完了し、借入資金をタイムリーに返済します。これにより、アービトラージャーの参入障壁が大幅に低下します。理論的には、誰でもアービトラージャーになれる可能性があり、無制限のアービトラージ資金を操作することができます。
上記のプロセスから、イーサリアム契約の制御権がFURUCOMBOフラッシュ契約-アービトラージャーアカウント契約-Uniswap契約-Kyberswap契約-フラッシュローン契約間で切り替わり、動的に対応する関数を呼び出すことができることがわかります。もしDeFiプロジェクトプラットフォーム契約に脆弱性がある場合、アービトラージャーはその契約の悪意のあるコードを利用して対応する関数を呼び出し資産を盗むことができます------契約間の状態同期情報の差を利用して、プロセスが終了する前に二重支払いを行ったり(イーサリアムの資産は単に値を代入するだけ)、あるいは許可されていないロジックを繰り返し実行して盗むことができます。
Moveエコシステムでは、資産は単純な値の代入ではなく、動的呼び出しと再入が禁止されているため、根本的にリスクを排除しています。その具体的な実現については後ほど説明します。
三:MoveとSolidityのフラッシュローンの具体的な実現にはどのような違いがあるのか?
3.1.イーサリアムフラッシュローンの両刃の剣:動的呼び出しと再入性
実現方法の観点から見ると、イーサリアムEVM(Solidityベース)は動的スケジューリングを持ち、再入を通じてフラッシュローンを実現できます。前回の報告『Web3基礎言語:MoveはSolidityのどの不足を補ったのか?』で述べたように:
"イーサリアム(Solidity)の資産は、対応する契約によって制御されます。Token A契約を金庫に例えると、金庫はすべてのユーザーに数値残高を割り当て、ユーザーが所有するToken A資産の数量を表現しますが、資産自体はToken A契約の金庫内に保管されています。一方、Moveユーザーアカウント自体が独立した大きな金庫であり、ユーザー自身が制御し、すべてのToken資産がこの金庫内に保管されています。これらのTokenは数値の形式ではなく、複製できず、ユーザーの制御を受けるリソース(タイプ)です。"
したがって、イーサリアムEVMがフラッシュローンアービトラージを実現するプロセスは次の通りです:
1)ユーザーは制御権をフラッシュローン契約に渡します;
2)フラッシュローン契約は外部のアービトラージ契約プログラム内の実行関数を呼び出し、リクエストされた借入金額をアービトラージ契約に送信し、アービトラージ契約はアービトラージ操作を行います;
3)アービトラージ契約はアービトラージを完了し、借入金をフラッシュローン契約に返還し、実行関数が作業を完了し、制御権がフラッシュローン契約に戻ります;
4)フラッシュローン契約は返済金額が正しいかどうかを確認し、正しければアービトラージ取引が成功し、そうでなければ失敗します。
上記のプロセスでは、対応する関数が動的に呼び出され、フラッシュローン契約は返済金額が正しいかどうかを確認する必要があるため、制御権の移転プロセスは随時発生する可能性があり、つまり再入可能です------同時に注意すべきことは、イーサリアムアカウントの資産は数値残高の形式で存在し、再入は二重支払いの可能性をもたらします。これが脆弱性のある部分です。外部契約を呼び出す主な危険の1つは、それらが制御を奪う可能性があることであり、外部の契約プログラムに脆弱性がある場合、攻撃者は再入攻撃を実現するために繰り返し呼び出すことができます。
再入攻撃は、イーサリアムの歴史上最も深刻な攻撃の1つを引き起こし、イーサリアムのフォークを直接引き起こしました。つまり、2016年6月17日のThe DAO崩壊事件です。ハッカーは契約を展開し、"投資者"としてThe DAOにいくつかのETHを保管しました。その後、ハッカーはThe DAO契約内のwithdraw関数を呼び出し、The DAO契約がハッカーに引き出しを行うようにしました。ハッカー契約(fallback関数)には悪意のある脆弱性が存在し------終了ロジックがなかったため、The DAOは引き出しを完了できず(この時点でThe DAOはハッカーが引き出しを受け取り、アカウント残高が0になったことを知りませんでした)、制御権を取り戻すことができませんでした。ハッカーはwithdraw関数を繰り返し呼び出し、初期の預金ETHの数量を超えて送信しました。
フラッシュローン攻撃事件では、攻撃者はしばしば悪意のある契約を通じて再入攻撃を実現します。例えば、2022年3月16日、ハッカーはフラッシュローンを借り、借入プロジェクトHundred Financeの脆弱性を利用してリアルタイムで再入攻撃を行い、最終的に約2363 ETHの利益を得ました。具体的なプロセスはそれほど複雑ではなく、Hundred Financeは先に送金し、後で記帳するため、ハッカーはフラッシュローンを借り、攻撃契約を通じてHundred Financeの借入プールに預け入れ、抵当ローンを実現しました。
攻撃契約が展開したonTokenTransfer関数は、記帳前に借入関数を繰り返し呼び出す(再入攻撃)ことで、1つの抵当資産から異なる借入プールから借入を引き出しました。先に送金し、後で記帳するため、記帳時(プロセス終了時)にはハッカーは攻撃を実現し、利益を得ていました。攻撃の核心は、プロセスが終了する前に、攻撃者契約が関連する関数を繰り返し呼び出し、資産を盗むことができるということです。
攻撃者契約が資産をA銀行(プロジェクトAの借入プール)に抵当してローンを受けると仮定しましょう。銀行Aは記帳結算を完了する前に融資を開始し、銀行Aが記帳結算プロセスを完了することはフラッシュローン取引が成功することを意味します(原子性)。この時点で資産はAに入庫され、記帳されます。
しかし、銀行Aと他の銀行間で情報が同期されていないため(契約関数間の状態が同期されていない)、攻撃者はA銀行が資産の入庫記帳を完了する前に(つまり、攻撃契約のローンプロセスが終了する前に、フラッシュローンの作業プロセスがまだ終了しておらず、契約関数の呼び出しが続けられる)、その資産をB、Cなどの他の銀行で再度抵当してローンを受ける(再入)ことができます。プロセスが終了すると、攻撃者はフラッシュローン攻撃を完了し、利益を得ています。
再入攻撃の前提は、攻撃者が展開した契約に悪意のあるコードが存在することですが、最も核心的な要因は次の通りです:
1) イーサリアム契約呼び出し時に制御権が移転するため、悪意のある契約に主導権を与えます;
2) プロセスが終了する前に再入(再呼び出し)が可能であり、悪意のある契約は脆弱性を利用して関数を繰り返し呼び出し、資産を盗むことができます(例えば、1回の引き出しプロセスが終了する前に何度も引き出す);
3) 加えて、イーサリアムアカウントの資産は数値残高の形式で存在するため、資産を繰り返し盗む(ダブルスピンドル)の可能性があり、またプロセスが終了する前に悪意のある契約が関連アカウントの資産残高を変更して盗むことができます。
再入はフラッシュローンを実現するための基 礎ですが、目標契約に脆弱性がある場合、攻撃者は再入攻撃を実施できます。これは前回の報告で詳述しました。
3.2.MOVEの"ホットポテト":再入のないフラッシュローン
Move言語は動的呼び出しと再入を禁止しており、これにより根本的に再入攻撃を排除しています。しかし、Moveシステムの資産はリソースタイプであり、一度借り出されると実際の移転が発生します。フラッシュローンがスムーズに返済されることをどのように確保するのでしょうか?
Moveはフラッシュローンの新しい実行プロセスを提案しました------ホットポテトモデルで、根本的に再入を放棄します。"ホットポテト(hot-potato)"モデルは、key、store、copy、dropの能力を持たない構造であり、Moveプログラムが取引実行中に一度だけ使用される構造です。drop、key、storeの能力がないため、ホットポテトは"破棄"関数を呼び出すことでプロセスを完結させることしかできません------これはその名の通り、ホットポテトであり、プロセス中のいかなる処理も"熱い手"であり、破棄関数に任せて完結させるしかありません。具体的なプロセスは以下の通りです:
1)フラッシュローンのスマート契約が作動中に"ホットポテト"(hot-potato)のレシート(receipt)を作成します;
2)アービトラージャーがフラッシュローン契約から貸付を受ける際、フラッシュローン契約は貸付資金とホットポテトのレシート(receipt)を送信します;
3)アービトラージャーは貸付資金を利用してアービトラージ操作を行います;
4)アービトラージャーが返済する際、返済関数(repay)を呼び出し、資金とレシート(receipt)を返済関数に送信し、レシートが返済関数に受け取られた後に破棄されます。
前回の報告で分析したように、アカウント資産とレシート(receipt)はリソースタイプであり、"複製、破棄、再利用できず、安全に保存および転送できる"ため、レシート(receipt)は処理される必要があり(かつ一度だけ使用される)、イーサリアムのようにアカウントに数値を代入するだけでは済みません。
したがって、ホットポテトモデルは借り出された資産(リソースタイプ、一度借り出されると実際に移転が発生する)を返還する必要があることを確保できます。レシート(receipt)はホットポテトとして、定時の引爆器のようなものであり(ここでの定時は、フラッシュローンアービトラージが原子取引としての"時間"であり、具体的な期間ではありません)、資金と引爆器が一緒に結びついて借り出され、いかなる一方も引爆器を保持することはできず、必ず元の場所に返さなければならず、そうでなければ取引は完了しません。したがって、フラッシュローン資金は返還されることが確保されます。
フラッシュローンが完了するための前提は、正しい返済とプロセスの終了であり、プロセスが終了する前に悪意のある契約がイーサリアムシステムで再呼び出しを実行し、関連する資産アカウントの値を変更して盗むことができます。Moveシステムのフラッシュローンプロセスが終了するための前提は、正しく資金を返還することに加えて、ホットポテトというリソースを一度限り回収して破棄する処理が必要であり、これがフラッシュローンの原子性を確保します。
アプリケーションの観点から見ると、Web3の基礎代表はオープン性と再構成可能性を保証しつつ、コードの安全性を高める必要があります。Web3では、コードは情報だけでなく、資産の呼び出しにも直接関与しており、ユーザー資産の安全を保証することが最も重要です。さもなければ、Web3は暗い森となります。イーサリアムのエコシステムは、スマート契約の活力を示しました。次の時代はこの基礎の上で安全性とコンプライアンスに向けて進化し続けるでしょう。これが私たちが現在Web3基礎言語の進化に注目している核心的な論理であり、次のイノベーションの波を育むものです。