当前位置: 首页 > Linux

Lisk沙箱漏洞分析及解决方案

时间:2023-04-06 22:47:18 Linux

背景前段时间,BitShares的创始人DanielLarimer质疑了lisk系统存在的一系列问题,其中大部分问题得到了lisk创始人之一Max的正面回应。可以看看这个http://ethereum.stackexchange...但是有一个问题Max没有回应或者还没有提出解决方案。那就是lisk侧链的运行环境。Larimer表示,“Lisk面临的大部分问题都可以通过高度定制化的JavaScript环境来解决。”否则,lisk的系统面临两大挑战:首先,lisk目前的沙箱机制不足以限制侧链代码的权限,也就是说,不可信的代码无法运行。这些代码可能窃取服务器的关键信息或直接损坏服务器。其次,关于侧链开发,或者说,lisk的侧链代码运行在javascript环境下,能力很全,有一些函数会带来不确定因素,比如Math.random。Lisk的官方开发文档特别指出,希望开发者避免使用这些功能。这会给开发者造成精神负担。如果是定制版的javascript环境,直接杀掉那些造成不确定因素的函数,开发者不需要额外花费精力去避开那些陷阱。Lisk的沙箱安全问题我们先来说说为什么要使用沙箱。沙箱(sandbox)是云计算平台广泛采用的一种安全防范措施。在中,应用代码是由各种第三方开发者实现的,他们的代码是不可信任的。需要一个沙箱来提供一层隔离,让这些应用程序代码只能做有限的事情。Lisk非常类似于区块链领域的云计算平台。他们提供一些服务,允许第三方开发者基于这些服务构建自己的应用程序(即dapps)。所以,lisk也需要一个沙盒机制来保证dapps的作者不能作恶。Lisk的做法是提供定制版的nodejs环境来实现这个目标。具体实现在这段代码里,https://github.com/LiskHQ/lis...经过分析,发现这个沙箱名不副实。它只是使用管道来实现进程间通信,它使用了迂回。的。nodejs的进程间通信可以直接通过javascript代码实现,为什么要用c++来实现呢?带着这个疑问,和一些期待,我亲自做了一个实验。我首先使用lisk-cli创建一个helloworld侧链lisk-clidapp-a然后我添加一段代码console.log(require("fs").readFileSync("../../config.json"))然后运行之,于是我得到了这个服务器主节点的受托人的所有密码"forging":{"secret":["wKyoJM1vS4ucHmWvxDSdcpC23mJwqfg3G6MKZoXaFfcnWHTqo7","2aTWYPpQidVunxTg3y8YESYps7za6f9d4wYn9Gy2GuGnE7JX7V","65uZNjL36Bdg2tkJnueYkd2n6YPe76fpdeYtgu7fso1m385mwD",………...Sure够了,这个沙箱并没有起到隔离的作用,拥有管理员权限,相当于给黑客打开了大门。有人说lisk系统有二级密码,你拿到了他的一级密码,还是抢不到他们的钱。也有人说,通过Linux自身的权限机制,可以将侧链代码赋予一个低级用户,使其无法访问其他用户的文件。这些都是治标不治本的办法。根本的解决办法是让沙箱名副其实,实现真正的环境隔离,让侧链代码对外界无知,就像lisk预想的那样。解决方案那么,如何实现真正的沙箱呢?解决方案有很多,比如跳过nodejs,直接使用v8引擎,或者使用进程级别的权限控制,比如windows系统中的SetWindowsHookEx。当然,lisk目前并不打算支持完整版的windows版钱包,所以在linux系统中可以使用seccomp技术。我这里有一个更简单的方法,就是使用nodejs自带的vm模块。第一步是创建一个原生的javascript虚拟机varvm=require('vm');varcontext=vm.createContext();vm.runInContext(sideChainCode,context);这几行代码完成了侧链代码的隔离,侧链代码中只能执行纯操作逻辑,只能使用v8引擎内置的少量javascript标准库。甚至没有setTimeout,console.log。我们需要做一些额外的工作。例如,将setTimeout和clearTimeoutcontext.setTimeout=function(fn,delay){if(typeof(fn)=='string'){setTimeout(newFunction(fn),delay)}else{setTimeout(fn,delay)添加到运行环境)}};context.clearTimeout=clearTimeout;增加日志打印功能,使侧链日志转发到主系统global.print=send.bind(global,'stdout');global.console={日志:发送.bind(global,'stdout')};global.process={stdout:{write:send.bind(global,'stdout')}};global.postMessage=send.bind(global,'message');另外,你也可以禁用那些导致不确定因素的函数global.Math.random=undefined;这些都做完之后,侧链代码的访问权限就被限制在一个很窄的范围内。他们不能使用nodejs内置的require、fs、http等标准库,这样的安全目的是达到了,但是又带来了另外一个问题,就是功能问题。不能使用那些额外的库。只有js的标准库太不方便了。很多复杂的功能无法实现,尤其是没有require之后,连模块化都做不了。所以我们需要第二步。第二步webpackwebpack原本是一种常用的前端打包方案,用于前端项目的模块化管理。很多人忽略了它同样适用于后端webpack,可以将node_modules中的库甚至一些nodejs内置的库打包在一起。也就是说,可以在前端使用的js库,除了UI相关,也可以在侧链沙箱中使用,比如async、bytebuffer、crypto、js-nacl、bignum等,这对于侧链来说已经足够了。不能使用的库,如文件系统、多处理和网络模块,正是我们要丢弃的。vm+webpack的组合堪称完美。这是千变万化的前端技术给javascript这门语言带来的好处,也是以太坊之类的solidity等新语言所望尘莫及的。但是,我们还需要做一些收尾工作。第三步是清除障碍。目前侧链代码中有些地方使用了一些比较复杂的库,比如ed2curve,涉及的依赖比较多。我们认为没有必要。这部分功能可以在主链中提供,通过进程间通信以API的形式提供给侧链。这也可以减轻侧链代码的负担,让侧链开发者更容易。这些代码对整个框架的影响很小,可以忽略不计,但是它们所依赖的库却占据了一半以上的代码,其中包括一些在沙箱环境中不允许的操作。经过分析,发现只需要禁用modules/api/crypto.js中的两个函数。使用,暂时手动修改,去掉fs相关代码,然后就可以正常打包运行了。最后,我在这里打包了一个完整的侧链工程和主链框架中的关键代码。http://o7dyh3w0x.bkt.clouddn....关于侧链和区块链开发的问题欢迎加群:485979564一起讨论交流