こんにちは!ブロックチェーンの世界へようこそ。最近、自分のデジタルアートやトレカを「NFT(Non-Fungible Token)」として発行してみたい、という開発者の方も多いのではないでしょうか?
でも、ニュースなどで「NFTの絵が後からこっそり書き換えられていた!」なんて話を聞くと、少しドキッとしてしまいますよね。「せっかくブロックチェーンに記録したのに、どうしてそんなことが起きるの?」って疑問に思うはずです。
今回は、そんなNFTの「メタデータ(絵の画像や説明文)」が裏でコソコソと改ざんされてしまう恐ろしい手口と、それを防ぐための最強の防犯対策「IPFSハッシュの固定とオンチェーン検証」について、身近な例えを交えながら一歩ずつ優しく紐解いていきましょう!
—
1. 家の鍵をかけたはずなのに…?NFTのメタデータ改ざんの仕組み
まずは、よくあるNFTの仕組みを「家の鍵と宅配ボックス」に例えて考えてみましょう。
ブロックチェーン(Ethereumなど)に記録されるNFTのスマートコントラクトは、いわば「絶対にピッキングできない最強の金庫」です。この金庫の中に、「私のNFTの絵は、この住所(URL)の場所に置いてあります」というメモが入っています。これが tokenURI と呼ばれる機能です。
一見すると、「金庫のメモが本物なんだから安心だよね!」と思いますよね。でも、ここに大きな落とし穴があるんです。
外部サーバー(Centralized Server)という名の「信用できない大家さん」
多くの初心者は、NFTの画像やメタデータ(JSONファイル)を、自分の持っているレンタルサーバーや、Amazon S3などのクラウドストレージに保存しがちです。そして、そのURL(例: https://my-awesome-shop.com/nft/1.json)を金庫のメモに書きます。
ここで、こんなシナリオを想像してください。
1. あなたが可愛い猫のイラストのNFTを販売しました。
2. 買い手は「金庫のメモにあるURLだから安心だ」と思って高額で買いました。
3. 数ヶ月後、あなたがそのサーバーの管理をサボった(あるいはサーバー会社が乗っ取られた)とします。
4. 悪意ある攻撃者があなたのサーバーに侵入し、URLの中身(JSONファイル)を「悪そうなゴリラの画像」にこっそり書き換えました。
ブロックチェーン上の金庫のメモは書き換わっていません(https://my-awesome-shop.com/nft/1.json のまま)。しかし、そのURLの先にある「中身のデータ」がいつの間にかすり替わってしまったのです!
買い手がウォレットを開くと、そこには可愛い猫ではなく、見覚えのないゴリラが映し出されます。これが、NFTメタデータ改ざんの恐ろしいメカニズムです。
—
2. 救世主「IPFS」と「ハッシュ値」という名の最強の指紋
「じゃあ、どうすればこの改ざんを防げるの?」という疑問が湧きますよね。そこで登場するのが、IPFS(InterPlanetary File System)という分散型のファイルストレージと、CID(Content Identifier:コンテンツ識別子)という技術です。
従来のWeb(HTTP)は「どこにあるか(住所)」でファイルを探しますが、IPFSは「中身が何であるか(内容の指紋)」でファイルを探します。
指紋認証のイメージ
例えば、警察の捜査で使われる「指紋」を思い出してください。人間が髪型や服を変えても、指紋が変わることはありませんよね。
IPFSのCIDもこれと同じです。ファイルの中身が1バイトでも書き換わると、生成されるCID(指紋)は全く別の文字列に変化します。
- 正しい猫の画像が入ったファイルのCID:
bafybeig...cat - 悪意あるゴリラに書き換えられたファイルのCID:
bafybeig...gorilla
もし攻撃者がサーバーの裏でこっそり画像を書き換えたとしても、中身が変わればCID(指紋)も変わってしまいます。つまり、「中身と住所が完全に一致していること」を数学的に証明できるようになるのです。
—
3. 実装で学ぶ!IPFSハッシュ固定とオンチェーン検証のやり方
それでは、実際にSolidityというプログラミング言語を使って、改ざんを許さない安全なスマートコントラクトを書いてみましょう。
ここで大切なのは、「URLの文字列そのものを自由に変えられるようにしないこと」です。コントラクトの中にIPFSのCIDをガッチリと固定(ハードコード)してしまい、後から書き換えられないように設計します。
以下のサンプルコードを見てください。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
// 読者の皆さん、一緒に安全なNFTコントラクトの書き方を見ていきましょう!
contract SecureNFT is ERC721, Ownable {
// 改ざんを防ぐため、IPFSのベースとなるCID(指紋のようなもの)をコントラクト内に固定します
// 例: ipfs://bafybeigdyrzt5sfp7d.../
string private _baseIPFSURI;
// コンストラクタで最初に一度だけ、改ざん不能なIPFSのURIを設定します
constructor(string memory initialBaseURI) ERC721("SecureArt", "SART") Ownable(msg.sender) {
_baseIPFSURI = initialBaseURI;
}
/**
* @dev トークンIDごとのメタデータURIを返す関数
* 外部サーバーではなく、最初に対策されたIPFSのパスを強制的に結合して返します。
*/
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
// トークンが存在するかどうかのチェック(OpenZeppelinの標準機能)
_requireOwned(tokenId);
// 固定されたIPFSのベースURIとトークンID(例: "1.json")を結合する
return string(abi.encodePacked(_baseIPFSURI, Strings.toString(tokenId), ".json"));
}
/**
* @dev 【重要】もし将来的にURIを変更できる関数(setBaseURI等)を用意してしまうと、
* そこが攻撃者から狙われる「裏口(バックドア)」になります。
* あえてこの関数を作らないことで、不変性(イミュータビリティ)を保証します!
*/
}
このコードのポイント
1. _baseIPFSURI の固定: コンストラクタで一度設定したら、それを変更する関数(セッター)をあえて作っていません。これにより、開発者自身であっても後からURLを悪意あるものに変更できなくなります。
2. オンチェーンでの組み立て: tokenURI 関数の中で、IPFSのベースアドレスと tokenId を組み合わせて強制的に返します。これにより、クライアント側(OpenSeaなどのマーケットプレイス)も常に正しいIPFS上のデータを参照し続けます。
—
4. 現場のセキュリティリサーチャーからの実践アドバイス
私たちセキュリティの現場から見ると、開発者がよくやりが的な「痛いミス」がいくつかあります。実務でスマートコントラクトをデプロイする前に、以下のチェックリストを必ず確認してください。
- 「後から変更できる機能」を安易につけない
「もしURLを間違えたら困るから、後から変えられる関数(setBaseURI など)をつけておこう」という親切心は、セキュリティの世界では最大の弱点になります。その関数に権限チェック(onlyOwner)の付け忘れがあったり、オーナーの秘密鍵が漏洩したりすると、一瞬で全てのNFTが改ざんされてしまいます。「一度デプロイしたら二度と変えられない(Immutable)」状態にすることが最高の防衛策です。
- IPFSの「ピン留め(Pinning)」を忘れない
IPFSは分散型ネットワークなので、誰もそのデータを自分のノードで保持していないと、ファイルが消えてしまう(アクセスできなくなる)リスクがあります。PinataやInfuraなどのIPFSピン留めサービスを利用して、確実にデータがネットワーク上に常駐するように管理しましょう。
—
まとめ
いかがでしたでしょうか?今回は、NFTメタデータの改ざんリスクと、IPFSハッシュ固定によるオンチェーン検証の仕組みについて解説しました。
- NFTの改ざんは、外部の中央集権サーバーを信頼していることが原因で起こる。
- IPFSのCID(コンテンツ識別子)を使うことで、ファイルの中身の「指紋」を固定できる。
- スマートコントラクト側でURIを不変(Immutable)に設計し、後から書き換えられないようにする。
セキュリティは難しく聞こえるかもしれませんが、一つひとつの仕組みを「身近な防犯」に置き換えて理解していけば、誰でも安全なアプリケーションを作ることができます。
一歩ずつ、確実にセキュアな開発スキルを身につけていきましょう!次回の記事もお楽しみに!
コメント