免责声明:本文不构成任何投资建议。
天生随机数不是该当留给人类来做的任务。
受访者:慢雾科技安全研究员Johan

特殊致谢:高金
作者:李画
文章来源:碳链代价
区块链的天下没有真正的随机数,但随机数却是区块链游戏之魂,至少在目前阶段。
以是,当伪随机数让DApp流下第一滴血后,DApp却无法断臂求生。潜伏的黑客们就像嗜血的鲨鱼,在嗅到腥味后迅速聚拢过来,围攻这个有着天生毛病的猎物。
Luckyos、EOS.WIN、DEOSBET、FairDice、EosRoyale、EOSDice、FFGame……这些热门DApp游戏先后被攻击,因随机数漏洞而面临死活危急。“没有被攻击过随机数的都不好意思说自己做的是DApp”,开拓者们如此自嘲。
本文采访了一贯关注DApp随机数漏洞的慢雾科技安全研究员Johan,并就代码细节问题咨询了资深DApp开拓工程师高金,试图通过这篇文章,呈现出随机数的前世与今生、原罪与救赎。
原罪:打算机的天下没有真随机数
真正的随机数只存在于物理天下中,比如掷骰子的结果,比如电子组件的噪声。
最早的随机数天生器是骰子,早在公元前2600年,人类就已经用四面骰来玩抛掷游戏了,迄今为止,它依然是最值得相信的产生随机数的方法。
但骰子显然不能知足当代天下对随机数的需求,于是在1995年,RAND公司推出《百万乱数表》(《A Million Random Digits with 100,000 Normal Deviates》)。RAND先通过随机脉冲发生器天生大量随机数,再把这些数字凑集起来组成随机数字书,免费供应给人们利用。
这本书是20世纪随机数领域的主要著作,也是人类历史上第一次产生如此大量的、高质量的随机数。
与此同时,阿兰·图灵在打算机 Ferranti Mark 1中首次内置了随机数天生指令,利用热噪声可以一次性天生 20 个随机比特。1999 年,Intel更进一步, 在 i810 芯片组上集成了芯片级的真随机数天生器(TRNG),通过热噪声产生本地源的随机数。
不过,TRNG每秒只能产生有限的随机比特,随机数天生速率低,致使软件的事情频率受限,而且TRNG对外界滋扰极其敏感,须要耗费大量功率避免非随机旗子暗记对旗子暗记源的污染。
伪随机数应运而生。冯·诺依曼创造了最早的伪随机数天生器(PRNG),通过给出一个确定的随机数种子,由确定的算法在每次天生同样的随机数序列。只要种子不变,伪随机数的数序也不会变。
自此往后,猜种子破解随机数便成为黑客的常规攻击手段,而事实证明,具有好的质量且不易被占领的种子很难寻觅。但即便如此,由于效率的缘故,打算机软件还是不得不依赖于伪随机数。
在当前的编程措辞中,C++、R、Python、Ruby、PHP等都采取了梅森旋转算法(Mersenne Twister)作为默认的伪随机数天生方法,它由松本真和西村落拓士在 1997 年发明。
梅森旋转算法把寄存器当时或者之前的状态作为种子来源,通过线性反馈移位寄存器对输入进行移位旋转,周期为一个梅森素数。该算法产生的伪随机数质量好、产生速率快。
伪随机数的种子来源也可以是真随机数——CPU通过TRNG不断产生真随机数,并将真随机数存储在熵池中,当软件须要利用随机数时,从熵池里提取一些真随机数作为种子输入到PRNG中,通过PRNG得到伪随机数序列。
天堑:区块链不支持天生随机数
传统的伪随机数天生算法或多或少与单台机器的物理状态或运算状态干系,不同的机器,或者说不同的节点,会有不同的运算结果,这在区块链上是行不通的。区块链是一个分布式的系统,它哀求各个节点的运算结果是可验证、可共识的。
区块链须要从零开始设计崭新的随机数法则,从而实现不同节点上的智能合约可以利用相同的随机数。
有三种办理办法。第一种是让可信第三方为合约供应随机数;第二种是通过根本合约实现伪随机数天生器,为其他合约供应同等的随机数;第三种是让所有节点上的合约可以采集到相同的种子,再通过伪随机算法打算出相同的随机数序列。
第一种办法最大的毛病是须要引入第三方。该第三方是否值得信赖,能否能供应高质量的随机数均是问题。除此之外,区块链是当心中央化的,而第三方在某种程度上是中央化的,这与一些DApp开拓者的理念不相符合。
在以太坊上,Oraclize是为链上节点供应随机数的第三方。Oraclize是一个预言机,独立于区块链系统之外,智能合约发送要求给Oraclize,当Oraclize监听到链上干系要求后,天生随机数并将结果返回区块链。
第二种办法最符合区块链精神,是一个不同参与者互助天生随机数的伪随机数天生器,但它涉及到勉励机制的设计问题,以及人为掌握的作弊问题。
RANDAO采取的即是这种办法,它以智能合约的形式封装了伪随机数天生算法和对应的业务逻辑,为以太坊供应随机数做事,任何人都可以参与天生RANDAO随机数。
在随机数天生周期,每个参与者都须要提交一个数字,而来自所有参与者的数字凑集将被作为种子天生伪随机数,由于无法知晓他人供应的种子,该方法产生的结果难以被破解。智能合约可以向RANDAO要求随机数,但须要支付奖金给天生随机数的参与者。
第三种办法,随机数不是从合约外部引入,而是把区块链的链上信息做为种子,由智能合约根据种子天生伪随机数。这种方法最大的毛病便是一旦黑客知道了随机数的天生算法,也能获取精确的种子,就能轻易地对智能合约发起随机数攻击。
不同于传统伪随机数天生算法中种子的私密性,区块链上的种子险些是“透明”的:它是链上的区块信息,所有节点上的智能合约都能够取到,那么从事理上讲,黑客用于攻击的恶意合约同样可以得到这些数值。
不过,由于缺少成熟的随机数供应方,以及对中央化随机数的当心、对链上自治的追求,通过智能合约打算随机数依然是EOS上浩瀚DApps首选的方法,这也是DApps深陷黑客危急的缘故原由所在。
攻防:道高一尺魔高一丈
FFGAME可能是史上最晦气DApp——游戏还没正式运营就遭到攻击。黑客很快破解了随机数,然后在游戏中不断得胜,轻松拿走FFGAME平台放入游戏中的初始资产,1332个EOS。FFGAME还没准备迎敌,城门就已失落守。
在DApp的攻防战中,攻击者们常日有两种方法来利用随机数盗取资产。
第一种方法是获取精确的随机数种子,通过伪随机算法打算出游戏结果,然后根据结果下注,担保百分百胜率。
第二种方法是在知道伪随机算法和种子来源的情形下,通过改变种子的数值让伪随机算法打算出自己下注的游戏结果,从而担保百分百胜率。
EOSDice是一个被黑客用第一种方法攻破后,修正了伪随机数算法,旋即又被黑客用第二种方法攻破的DApp。不过,值得讴歌的是,EOSDice也是一个被攻破两次但依然坚持开源的 EOS游戏。
EOSDice 第一次被攻击发生在11月4日 上午3:15,攻击者为jk2uslllkjfd,共被盗取约2500个EOS并转入火币。
EOSDice的伪随机数天生算法中利用的随机种子紧张是: tapos_block_prefix() ; tapos_block_num() ;name (); game_id ;current_time(); pool_ol_eos.amount。种子中的后四个,也便是帐户名、id、开奖韶光、合约余额都比较随意马虎获取,随机数的安全性紧张依赖于前两个种子,也便是reference block的信息。
在EOSDice 的实时开奖机制中,开奖action的reference block在默认状态下是实行当前action的上一个区块,该区块已经存在,其信息可以提前获取。因此,黑客可以通过种子预先算出结果,再下注。
EOSDice在被攻击后把实时开奖改为异步延时开奖,并重新上线运营。只不过二次攻击很快发生,在11月10日上午11:19,账户名为coinbasewa11的攻击者盗取了约4900 个EOS,并转入bitfinex。
在异步延时开奖机制下,reference block的指向发生了变革。开奖action的reference block是不才注时还未天生的区块,其信息难以提前获取,也就不能抢先打算出游戏结果。
但黑客们谋划了新手段:首先,让攻击合约仿照EOSDice的游戏合约,只要两种合约运行在同一个区块,就会取到相同的种子,打算出相同的结果;然后,由于EOSDice伪随机算法的种子包括账户余额,黑客可以在攻击合约中一次次修正余额的数值来改变种子进而改变运算的结果,直到其终极知足自己的下注条件;末了,在通过上述打算“碰撞”出“精确”的账户余额后,黑客只须要向真正的游戏合约账户转入被打算好的EOS,就能担保开奖时一定中奖。
救赎:没有最好,只能更好
链上随机数问题没有完美的办理方案。区块链上不仅没有真随机数,连传统的伪随机数也没有。
EOS官方示例中采取的随机数天生法类似于上文谈论的第二种办法——用不同参与者的私密数据的凑集作为种子,天生难以被预测的伪随机数。
以Dice为例,玩家和庄家须要提前天生密钥,把公钥先传到链上,开奖的时候再提交各自的私钥,作为种子天生随机数,再由随机数剖断开奖结果。这可能是目前最安全的伪随机数天生办法,但它给玩家增加了额外的且并不轻松的操作,提高了游戏门槛,在现实中并未被广泛的采取。
BM在EOS开拓者群回答随机数安全问题时,提出了一个“信赖区块生产者”的方案,也便是说用区块生产者在打包交易的某个特定时机获取的某些信息来天生伪随机数。虽然是在链上办理问题,但这种方法更靠近于上文谈论的第一种办法——由一个中央化的第三方来供应随机数,虽然难以被破解,但供应者不一定值得信赖。
屡败屡战的EOS上的DApp开拓者常日选用上文中的第三种办法——智能合约获取链上数据作为种子,自己天生伪随机数。
在与黑客数次交手之后,目前DApp最常用的开奖机制是“二次延时开奖+种子中不设置可控变量”。
实时开奖机制下,reference block是上一个区块,个中的种子数据在开奖前就可被黑客获取;而二次延时开奖,reference block是还未天生的区块,个中的种子数据难以预测,黑客也就无法提前算出开奖结果。种子中不设置可控变量则担保了黑客不能通过改变种子的数值来操控开奖结果。
不过,即便如此,“二次延时开奖+种子中不设置可控变量”也不能担保合约随机数的绝对安全,只能说在目前阶段这种方法还未被黑客攻破,相对安全。毕竟不管采取几次延时,开奖合约能获取的种子,攻击合约也一样能得到,链上的种子是“公正”、“透明”的。
如何在这种不完美的情形下只管即便减少随机数攻击带来的毁坏,慢雾团队给出了一些实用建议:
1.多用攻击者视角审查合约。
2.攻击者一样平常是通过恶意合约完成攻击,要思考合约能从什么角度攻击随机数。
3.随机数的安全与伪随机数天生算法干系,也与开奖机制干系,算法和机制要合营设计。
4. 伪随机数天生算法不要引入可预测种子和可控种子,避免结果被预测或被修改。
5. 理解区块链上的随机数与传统随机数的差别。
6.做安全审计。在项目上线和开源前做安全审计,已知的漏洞能被查出,未知的漏洞也能通过风控机制把危害降到最低。
结语
“在所有的产生随机数的事物中,我认为没有什么能够超越骰子了”,Francis Galton在 1890 年的《自然》杂志中如此写道。
但骰子永久也不可能知足打算机运用的需求,一旦要在二进制的天下利用随机数,我们就须要做出妥协,把自己暴露在黑客攻击的危险之下。
区块链天下的分外性须要我们做出更多的妥协。由于分布式、由于透明、由于当心中央化与渴望链上自治,在区块链上,没有真随机数、也永久不会有绝对安全的随机数。
随机数带来的是一场永久的战役,在开拓者与黑客之间。随机数会抗所有已知类型的攻击,但黑客也会不断创造新的攻击手段。此地无人生还。
参考:
1.《道高一尺魔高一丈的随机数攻击—— EOS伪随机数漏洞》
2.《EOS主网上线以来智能合约攻击办法汇总》
3.《Fomo3D 狼人杀、EOSBet 、EOSDice 等十八个安全漏洞事宜始末》