こんにちは!ブロックチェーンの世界へようこそ。
スマートコントラクトの開発、なんだか魔法のように自分の思い通りにプログラムが動いてワクワクしますよね。「よし、完璧なアプリができたぞ!」とデプロイしたあとに、思わぬ落とし穴でパニックになってしまう新人開発者の方は少なくありません。
今回は、スマートコントラクトのセキュリティにおけるちょっと怖い、けれどとっても重要なテーマ「コントラクトの自己破壊(selfdestruct)による資金凍結」について、身の回りの防犯に例えて一緒に優しく紐解いていきましょう!
一歩ずつ対策を学んでいけば絶対に怖くありませんので、リラックスして読んでくださいね。
—
1. 家の鍵に例えて理解する「selfdestruct(自己破壊)」の恐怖
突然ですが、あなたが新しいマイホームを建てたと想像してください。
その家には、近所の人がいつでも自由に使える「共同のお財布(貯金箱)」がリビングの真ん中に置いてあります。みんな「ここに預けておけば安心だね」と、大切なお金をそこに貯めていました。
さて、ある日、建築を担当した大工さん(コントラクトの作者)が、うっかり(あるいは悪意を持って)、こんな仕様の魔法のスイッチを残してしまっていたとしたらどうでしょう?
> 「このスイッチを押すと、家ごとこの世から綺麗に消滅します。家の中にある現金は、すべて指定した私のポケットに移動します」
…こんなの、怖すぎて夜も眠れませんよね!
ブロックチェーンの世界における selfdestruct という機能は、まさにこれと同じことを引き起こします。
コントラクト(プログラム)に selfdestruct(受取人のアドレス) という命令が実行されると、そのコントラクトはブロックチェーン上から強制的に消去され、まだコントラクトの中に残っていたETH(イーサリアムなどの仮想通貨)は、強制的に指定されたアドレスへ送られてしまいます。
もし、別のコントラクトがこの「消されたコントラクト」にお金を預けっぱなしにしていたり、依存していたりしたらどうなるでしょうか?
依存していたプログラムは「あれ?連絡先(コントラクト)が消えているぞ?」となり、機能不全に陥ってお金が二度と引き出せなくなってしまうのです。これが「資金凍結」の正体です。
—
2. 攻撃者はどうやってこの弱点を突くのか?
実際のスマートコントラクトのコードを見てみましょう。
以下は、危険な selfdestruct を含んでしまったサンプルコードです。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 【危険な例】自己破壊機能を持ったコントラクト
contract VulnerableVault {
// 資金を預かる機能
mapping(address => uint256) public balances;
// 誰でも(あるいは特定の条件で)お金を預けられる
function deposit() public payable {
balances[msg.sender] += msg.value;
}
// 【危険なスイッチ】コントラクトを破壊して、残金を全て回収する
function destroyVault(address payable _to) public {
// ここが実行されると、このコントラクトは消滅し、残高はすべて _to のポケットに入ります
selfdestruct(_to);
}
}
攻撃者は、もしこの destroyVault 関数が外部から誰でも呼べる状態になっていたり、オーナー権限の管理がガバガバだったりした場合、隙をついてこの関数を呼び出します。
すると、コントラクトは一瞬で消え去り、ユーザーが預けていた大切な資金は攻撃者のウォレットへ吸い取られてしまいます。さらに、この VulnerableVault を信頼して連携していた他のプログラム(DAppsなど)は、宛先を見失って完全にフリーズしてしまうのです。
—
3. 現在はどうなっているの?「EIP-6780」という救世主
「えっ、そんな怖い機能が今でも放置されているの…?」と不安になりますよね。安心してください。Ethereumの開発者たちも、この問題にはずっと頭を悩ませていました。
そこで導入されたのが、EIP-6780 というアップデート(Dencunアップグレードに含まれる重要な変更)です。
簡単に言うと、最近のEthereumのルールでは、「そのトランザクション内で新しく作られたコントラクトでしか、selfdestruct は本当の意味でコントラクトを削除できない」ように制限されました。
昔のように、「何ヶ月も稼働していて、みんなが大切なお金を預けているコントラクトを、ある日突然消し去る」という乱暴なことが、非常にやりにくくなったのです。
しかし、「ルールが変わったから、もう何もしなくて大丈夫!」というわけではありません。古いバージョンのコードを使い回したり、別のレイヤー2チェーンや独自のEVM互換チェーンで開発していたりする場合、依然としてこのリスクが潜んでいる可能性があります。
—
4. 私たちが実践すべき安全な設計と対策
では、私たち開発者はどのように身を守れば良いのでしょうか?
実務で絶対に守るべきポイントを整理していきましょう。
① 原則として selfdestruct は使わない
現代のスマートコントラクト開発において、特別な理由(例えば、ガスの最適化や極めて高度なプロキシパターンの実験など)がない限り、selfdestruct をコードに書く必要性はほとんどありません。「使わないこと」が最高の防犯対策です。
② アップグレード可能な設計(Proxyパターン)を活用する
「もしプログラムにバグが見つかったら修正したいから、消せるようにしたい」という理由で selfdestruct を使いたくなるかもしれませんが、それは間違いです。
バグの修正や機能の変更を行いたい場合は、selfdestruct ではなく、Upgradeable Proxy(アップグレード・プロキシ)パターンを使いましょう。これは、家の構造自体はそのままに、中の家具や内装だけを安全に入れ替えるようなモダンな手法です。
③ 外部コントラクトへの依存関係に注意する
自分のコントラクトから、他のコントラクトへ資金を預けたり処理を任せたりするときは、「その相手が突然消えても大丈夫な設計になっているか?」を必ず確認(セルフレビューや監査)するようにしましょう。
—
まとめ
今回は、スマートコントラクトの selfdestruct による資金凍結リスクについて、身近な防犯に例えて解説しました。
selfdestructはコントラクトを消滅させ、資金を強制移動させる諸刃の剣である。- 依存しているコントラクトが消されると、資金が凍結・消失して大パニックになる。
- EIP-6780によってリスクは軽減されたが、基本は「使わない」「安全なプロキシ設計を学ぶ」ことが重要。
セキュリティの世界は一見すると難しそうに見えますが、「どうすれば泥棒に入られないか」「どうすれば家を守れるか」というロジックは、現実世界の防犯とまったく同じです。
一歩ずつ、安全で信頼されるスマートコントラクトを作れるエンジニアを目指して一緒に頑張っていきましょう!次の記事でも実務に役立つセキュリティの知見をお届けしますので、お楽しみに!
コメント