区块链安全


Layer 0

日蚀攻击Eclipse Attack


黑客通过僵尸网络把你孤立了起来,然后让你重启,这样就可以实现DoS攻击,和双花

但是这个攻击要求太高

Request Timeouts


如果黑客只发送Hash给节点,但节点求Block后黑客就不反回任何值,这个节点需要等到timeout才能进行下一个交易。

如果有足够多的僵尸网络给节点发Hash且不回复,那么就形成了DoS攻击。或者重复发送同一个Hash,也不回复。

Transaction Timeout

  1. 成为第一个发送这个transaction的人
    • 如何成为第一人?:增加攻击者的连接数
  2. 节点会有timeout机制
    对于Transaction:FIFO机制,所有把我们自己全进去

Block Time

对于Block:因为等待二十分钟后断开连接

  • 受害者的要求
    1. 不能接收到Header
    2. 不能接收到version消息(两个节点相互交流,告知对方自己到了多少Block了,如果有差异就请求对方发送给自己所缺的header)
  • 攻击者的要求:
    1. 第一个返回block的人
    2. 不让受害者接收到其他的peer消息

黑客可以找准一个时刻,当受害者没有接到到B+1的时候,发送给他这个B+1的hash,然后就不管他了。然后在第一个timeout结束前,再发送B+2的hash。因为这个较短的时间段,使得受害者无法接收到其他peer发送的version消息。这样循环,受害者就不能动了。

如果黑客选择在B+2的timeout结束前停手,那么受害者第一个接受到的是B+3的hash,然后是B+2,最后才是B+1,也就是说他还是得等足时间才能从被攻击走出。

实际操作:双花

0 Confirmation Transaction

  1. 黑客在Bitcoin Network发送了TXd,TXd是TXl的双花。然后他悄悄地在别人之前发送了TXd的hash给商家
  2. 然后黑客不给商家发送TXd这个transcation,形成timeout,这样Bitcoin Network也就联系不上商家。
  3. 黑客去店里,付钱TXl买了杯咖啡,如果这个商家不检查(0 confirmation) 就提供了服务,那么只有攻击结束时,他发给Bitcoin Network才知道被骗。

    1 Confirmation Transaction

如何加强防范

  • 动态的timeout间隔
  • 处理transcation传播:
    • 过滤IP地址
    • 随机选择谁送的hash
  • 更新Block传播:
    • 广播header而不是Hash,这样就可以验证了
    • 记录Block的传播者,重复出现可能有问题。

Layer 1

关键在于共识机制。
不同的区块链的PoW不同在两个方面:

  • Block Generation
  • Block Size
Bitcoin Ethereum
Generation 10分钟 15秒
Size 1 MB 根据Gas Limit决定

直观上来说,更快的区块生成意味着更快的交易速度,更大的区块,意味着更多输入/更慢的传播。有什么不好的地方么?

快速的生成,意味着很多矿工会算到同一个区块,但只有一个人能获胜,那么其他人的block就是stale,算力就被浪费了。比如Bitcoin的Stale Rate为0.4%而Ethereum的Rate是6.8%

所以我们就会有下面的困境:

  • 生成速度越快可以有更快的支付,区块越大传播更慢,两者都导致security降低
  • 生成速度越慢就会降低支付速度,区块越小传播越快,两者都导致security提升

那么我们就想比较一下,不同的生成和大小组合到底对安全性有什么影响:

我们设置了Markov Decision Process(MDP)来奖惩黑客的行为:

  • 黑客的Action:继续挖矿或停止
  • 黑客的Reward:自己添加的钱,block reward,mining fee

有了这些我们就可以模拟黑客的行为了,因为如果计算需要话很多时间而reward很小的话,黑客就没有理由去费力气去做双花的计算。

Layer 2

Smart Contracts只能合约

  • 出入钱
  • 在以太链中执行
  • 用高级语言写得
  • 公开后就不能修改打补丁

重入攻击 Reentrancy

Wallet

1
2
3
4
5
6
7
8
9
10
contract Wallet{
uint balance=10;

function withdraw(){
if(balance > 0){
msg.sender.call.value(balance)();
}
balance=0;
}
}

User Contracts

1
2
3
4
5
6
function moveBalance(){
wallet.withdraw();
}
function() payable{
wallet.withdraw();
}

利用了Solidity的特性:如果contract中没有一个函数用到payable,那么就会有一个隐函数生成function() payable{//log payment},然后当使用call.value转移钱的时候,就会自动调用那个payable函数。

那么我们就可以override这个payable函数,让他回调withdraw,那么就会不断迭代。在call.value那一行中,又因为上一行的balance只是local variable,不是我们真实的balance,这样会把我们的钱转光。

这也就是DAO攻击。

未授权的写入

1
2
3
4
5
6
7
8
9
10
11
12
contract Wallet{
address owner = ....;
function initWallet(address _owner){
owner = _owner;
}

function withdraw(uint amount){
if(msg.sender == owner){
owner.send(amount)
}
}
}

本意是添加这个合约的owner,来限定只有owner可以转走钱。但是initWallet没有限定,于是任何人都可以重新把自己定义为owner,然后转走钱。