# 【1024献礼】生成可掌握钱包和揭秘NFT和Token被盗案例以及如何预防被盗

By [vadxq](https://paragraph.com/@vadxq) · 2022-10-24

---

> 最近圈内钱包被盗的事件频发，有人NFT被盗，有人token被盗了。其实大家都有自己的心理防备，只是有些新花招防不胜防呀！之前的一些被盗需要操作链上交易，这个时候需要交gas费，要交钱的地方每个人都会稍微迟疑一下，新的招式大部分就是签名就盗取了资产，很容易让人上当，导致NFT和Token资产被盗走了，损失了一大笔财产，心疼不已呀！正值1024程序员节，就斗胆为大家献上一份近期案例的分析和一个生成完全掌握且安全的使用hd钱包的方法。

前提概要
----

*   **区块链**：是一种安全共享的去中心化的数据账本。区块链技术支持一组特定的参与方共享数据。具有去中心化，不可纂改的特性。
    
*   **钱包**：存储以比特币为首的密码货币的公钥与私钥、私钥所对应的地址、该地址（群）的货币结算，以及货币交易的支持系统。说白了就是一个地址id，这个id记录了你的交易信息，你的钱，你的资产，只能你自己拥有对他的更改操作权限。
    
*   **Gas费**：是用于测量在以太坊区块链上执行特定操作所需的成本。就相当于付小费别人才会给你记账。
    
*   **Token**：可以粗俗的理解为区块链的货币。
    
*   **NFT**：非同质化通证。NFT的重要特征是独特有唯一标识，两两不可互换，最小单位为1且不可分割。可以粗俗的理解为一张票。
    

被盗案例
----

一般黑客会Copy做出一个很像的网页，然后伪装一个公开账户比如微博或者推特，非常像，连域名都很像，很多仅有一个单词不一样，或者某个单词顺序换了一下。有相当一部分用户就被迷惑了。特别是盲签，只有一段加密后的字符串，没有其他信息可以判断。

目前两种资产容易被攻击者盗取：一种是NFT，一种是token。这两种资产被盗的原理也是不一样的：

### NFT被盗

通常我们购买NFT后，如果价值涨了，会在Opensea上挂出进行出售。当你在Opensea上架的时候，会要求你授权Opensea，让它拥有你的这个项目collection的NFT执行approve方法的权限。作为第三方交易平台，要求这个权限其实也还算正常阶段，毕竟需要帮你托管，也需要你交gas费用。但是这里出现了一个信任危机问题，Opensea的NFT交易协议Seaport是开放的，只要你可以拿到用户的签名信息，它就会基于是你签名的原则，从而导致任何人都可以通过调用这个协议来骗取你的签名，然后将你的NFT挂单0价格出售给了攻击者的账户。以此来实现迷惑用户，将用户NFT资产转走。

### 怎么预防？

其实这个我们多加一个心眼，就很好预防了。我们可以看下面两个图，分别是官网和钓鱼网站通过seaport协议唤起的签名。我已经画红框圈出来了，我们可以对比这两个有啥区别。最大的区别就是域名。会有提示是哪个网站唤起的签名。第二个域名明显不是Opensea的，完全可以断定是钓鱼的，看到这个你就要小心了，在你没有确认这个网站真实性的时候，千万不要点击sign签名！同时域名一定要看仔细，很多钓鱼网站会利用一字之差，或者换了个顺序来达到迷惑用户，所以一定仔细看！

![1](https://storage.googleapis.com/papyrus_images/99d89258c5a2b2ea0c452c1e05a36745fdb6d76e4fb227fc7f0672e3f8be1a6c.png)

1

![2](https://storage.googleapis.com/papyrus_images/fd5a2f6e2e2480a6452dc1c0e059bf3f44830793b3b86de6d89a2564b015ccb4.png)

2

### Token被盗

攻击者会利用 **盲签(eth\_sign)** 这种方式，盗取用户的token资产。这种钓鱼方式具有很强的迷惑性，以前碰到的授权类钓鱼会展示出攻击者所要我们签名的数据且需要gas费用。但现在这种只有一段没有特征的字符串，一旦sign签名了，他就会将你的token转走，如下图：

![3](https://storage.googleapis.com/papyrus_images/789618a6b291f78db486b9bcc1eed973c1103844c56724cebacbc22fc1cc57f6.png)

3

那么它是怎么盗走我们的token的呢？首先攻击者通过小狐狸和第三方查询连上数据服务共同验证地址资产，这是所有钓鱼网站都会做的事情，分析你的所有资产。然后分析出我们拥有的token是攻击者想要的。其中调用了与JSON RPC直接交互的接口eth\_call，然后再通过eth\_sign盲签拿到了签名，然后就GG了。

那么他是怎么和JSON RPC交互的呢？我们可以回顾一下，交易是怎么进行的：假设我们使用小狐狸等钱包应用进行交易，在广播前，钱包会自动获取和计算出我们的转账对象to，金额value，还有附带数据data，以及 nonce、gasPrice、gasLimit 参数进行 RLP 编码，得到原始交易内容（rawTransaction）。而如果使用合约调用的话，那么转账对象to就是合约地址，data 为调用数据。随后对此内容进行 keccak256 哈希，得到一串 bytes32 的数据signData，这个signData就是我们就是所需要我们签名的数据了。如果我们这时候，对这个唤起的sign签名的话，那么就会发生危险的事情，小狐狸钱包会对这串数据签名后就会得到 r, s, v 值，然后用这三个值再与nonce/gasPrice/gasLimit/to/value/data 进行一次 RLP 编码，就可以得到签名后的原始交易内容了，这时候交易就广播出去了，基本上就是GG了。

![4](https://storage.googleapis.com/papyrus_images/0b52350cc9e5c96881ead3cb1c9edd50b4644bd99771c33ddd7fd51b78ca6bbb.png)

4

### 如何防盗

其实目前这种接口已经不会有正规网站或者服务会用了。小狐狸官方解释是因为还有一些管理端需要使用到这个盲签接口。所以，我们只要对所有调用盲签接口都不可信即可。基本上不会有这样的服务了。而且小狐狸其实也会在签名的位置有红色提醒文字，看到这个就就可以点拒绝了。

HD钱包
----

### 助记词泄漏

还有一种钓鱼网站，会迷惑你交出你的助记词，从而导致整个钱包账户进入风险之中。那么我们对于自己的助记词一定一定不能告诉第三方！当然，我们也可以自己生成一个HD钱包，而且多生成几次，还可以生成一个稍微好看点的钱包地址！我就是一个为了一个稍微好看的钱包地址生成了好多遍的无聊人士。

### 钱包地址原理

钱包地址的原理,大家可以看参考文章\[4\][https://huangwenwei.com/blogs/bip32-bip39-and-hd-wallet](https://huangwenwei.com/blogs/bip32-bip39-and-hd-wallet)去详细阅读，这里就只简单介绍一下：

钱包的组成：

**Address + Private key = Wallet**

根据密钥之间是否有关联可把钱包分为两类：**nondeterministic wallet** 和 **deterministic wallet**

*   nondeterministic wallet：密钥对之间没有关联
    
*   deterministic wallet: 密钥对由一个原始的种子主密钥推导而来。最常见的推导方式是树状层级推导 (hierarchical deterministic) 简称 HD
    

目前HD钱包是大多是比如imToken钱包应用等采用的。遵循BIP32/39协议，

BIP32就是：**为了避免管理一堆私钥的麻烦提出的分层推导方案。**

根据BIP44协议让地址私钥管理更方便，扩展了对多币种的支持。

BIP0044指定了包含5个预定义树状层级的结构：

    m / purpose' / coin' / account' / change / address_index
    

**m**是固定的,。**Purpose**也是固定的，值为44（或者 0x8000002C）。**Coin type**这个代表的是币种，0代表比特币，1代表比特币测试链，60代表Ethereum。**Account**代表这个币的账户索引，从0开始。**Change**常量0用于外部(收款地址)，常量1用于内部（也称为找零地址）。外部用于在钱包外可见的地址（例如，用于接收付款）。内部链用于在钱包外部不可见的地址，用于返回交易变更。(所以一般使用0)。**address\_index**这就是地址索引，从0开始，代表生成第几个地址，官方建议，每个account下的address\_index不要超过20。

### 助记词与HD

这是生成助记词的原理图：粗俗的描述就是通过生成一个128位的随机字符串和4位的随机数，然后得到132位的随机字符串，然后通过进行11位的拆分，得到12个区块，每个区块对应BIP协议词库的一个单词，然后得到一个12个单词的助记词。

![5](https://storage.googleapis.com/papyrus_images/8434855d56ba000362e01205ab837b5b36e3a76642c99197f97b63abea7702f5.png)

5

然后通过助记词来生成也一个512位的种子seed字符串

![6](https://storage.googleapis.com/papyrus_images/0cb5fb31405032c29b935d94915b0943dde0feae9b288fe2852f1d7ca72d1117.png)

6

再利用种子生成主地址的公私钥

![7](https://storage.googleapis.com/papyrus_images/6466c18081243b0f41ca90a4b269df8a3fbe97e02c98a078923bb966a42316af.png)

7

通过bip44协议，可以通过父元素的信息来生成子私钥和子公钥：

![8](https://storage.googleapis.com/papyrus_images/4240f0b9439f6fee9eff421859d9a262f757dcd9639884590d21ce83fa3eca34.png)

8

实战生成钱包
------

这里只是简单的罗列一个eth生成的方式，更多token生成方式，比如BTC/SOLANA/中文助记词等可以查看以下代码库：

[https://github.com/vadxq/generate-hd-wallet](https://github.com/vadxq/generate-hd-wallet)

生成之后，我们就只用导入单个地址的密钥即可。即使这个钱包被盗，也不影响其他钱包，真正实现一个助记词管理多个地址。

    const bip39 = require('bip39');
    const { hdkey } = require('ethereumjs-wallet');
    const util = require('ethereumjs-util');
    const args = process.argv.slice(2);
    console.log(args);
    
    // 1.mnemonic生成助记词.第一次使用的时候
    // English mnemonic
    let mnemonic = bip39.generateMnemonic();
    
    // 2.将助记词转成seed
    const getSeed = async () => {
      let seed = await bip39.mnemonicToSeed(mnemonic, args[0]);
      // 如果已经有了助记词，想复原地址，那么用这一行
      // let seed = await bip39.mnemonicToSeed(args[1], args[0]);
      console.log('seed：' + util.bufferToHex(seed));
      return seed;
    };
    // 3.提取私钥，公钥，账户
    const obtainAccount = async () => {
      let seed = await getSeed();
      // 通过hdkey将seed生成HD Wallet
      let hdWallet = await hdkey.fromMasterSeed(seed);
    
      for (let i = 0; i < 5; i++) {
        // 生成钱包中在m/44'/60'/0'/0/i路径的keypair ethereum
        let key = await hdWallet.derivePath("m/44'/60'/0'/0/" + i);
        // 从keypair中获取私钥
        console.log('私钥：' + util.bufferToHex(key._hdkey._privateKey));
        // 从keypair中获取公钥
        console.log('公钥：' + util.bufferToHex(key._hdkey._publicKey));
        // 使用keypair中的公钥生成地址
        let address = await util.pubToAddress(key._hdkey._publicKey, true);
        //编码地址
        console.log('account', i + 1, '0x' + address.toString('hex'));
    
        console.log('- - - - - - - - - - - - - - - - - - - - - -');
      }
    };
    
    obtainAccount();
    

彩蛋
--

哈喽！Buidler们！最近一直在酝酿一个开放区块链记录平台。想营造一种Buidl精神的氛围社区，可以让我们更好地认识一些有志之士，一起与志同道合者共同去Buidl未来！目前域名已注册，就叫buidl.wiki。希望能以wiki的方式去承载一同buidl世界的人与事。希望来年的1024节的时候，能让大伙体验上！

![buidl.wiki](https://storage.googleapis.com/papyrus_images/3d8d86f5e346a2d6e36f1730c01883f838d5fdec6b4ee8050978e034ad5a1b72.png)

buidl.wiki

By: vadxq 于2022.10.24 上海

参考文章

\[1\] [https://mp.weixin.qq.com/s/gRwkEYsO5pQ2hY7Pp5CIvA](https://mp.weixin.qq.com/s/gRwkEYsO5pQ2hY7Pp5CIvA)

\[2\] [https://mp.weixin.qq.com/s/E-LSN5eYwWhCQOH46-XyNg](https://mp.weixin.qq.com/s/E-LSN5eYwWhCQOH46-XyNg)

\[3\] [https://learnblockchain.cn/2018/09/28/hdwallet](https://learnblockchain.cn/2018/09/28/hdwallet)

\[4\] [https://huangwenwei.com/blogs/bip32-bip39-and-hd-wallet](https://huangwenwei.com/blogs/bip32-bip39-and-hd-wallet)

原文首发：

博客：

[https://blog.vadxq.com/article/generate-wallet-anti-theft/](https://blog.vadxq.com/article/generate-wallet-anti-theft/)

微信公众号推文：

[https://mp.weixin.qq.com/s/VsZGA64b1djEydh2y4MMhQ](https://mp.weixin.qq.com/s/VsZGA64b1djEydh2y4MMhQ)

---

*Originally published on [vadxq](https://paragraph.com/@vadxq/1024-nft-token)*
