NPM Security
References:
https://blog.npmjs.org/post/180565383195/details-about-the-event-stream-incident
https://hackernoon.com/im-harvesting-credit-card-numbers-and-passwords-from-your-site-here-s-how-9a8cb347c5b5
https://hackernoon.com/part-2-how-to-stop-me-harvesting-credit-card-numbers-and-passwords-from-your-site-844f739659b9
https://medium.com/@cnorthwood/todays-javascript-trash-fire-and-pile-on-f3efcf8ac8c7
https://www.reddit.com/r/programming/comments/a0kxmw/i_dont_know_what_to_say_backdoor_in_popular/
重新阅读了上面的HackerNews博客。
没错,我也光荣中招了——nuxt的依赖npm-run-all,vue-cli的依赖ps-tree。这些包的依赖event-stream被引入了攻击包:flatmap-stream。
在NPM包event-stream中招后,重新思考了对于npm和Node社区的一些看法。
原文作者提到了如果在页面涉及核心信息,尽可能减少任何形式的、任意形式的包依赖。并且需要彻底去除任何有可能操作/监听这个页面的任何方法。
原文中提到的解决方案有:
- 通过
<a href>
转到一个安全的、非同域的页面中,该新页面中不包含任何依赖库、依赖包、预处理/后处理器。 - 通过
<iframe>
标签启动一个新的子窗口,并且设定sandbox
属性保证外部无法监听内部的信息。 - 实现CSP。
上图说的是一种典型的绕过CSP的方式——通过prefetch类型的link元素,添加到Head中。由于Head的元素优先级与CSP相同,这样就可以非常轻松的绕过CSP的限制。 同样的,用JS操作CSP头也只是非常轻松的。
另外个人认为使用iframe的方式也并不完全安全——只需替换iframe的src为完全相同样式的钓鱼页面即可。而且iframe是不带地址栏的,这样更难发现钓鱼的入侵。
由于现在前端的广度逐渐扩大,从编译领域到浏览器领域,从node模块到condova,从CLI到GUI,攻击向量可谓前所未有之大。这次是选择进行攻击开发者copay-dash
的供应链,下一次很有可能就是攻击开发者的npm registry,使用更加难以被察觉的方式——如感染外部包的形式,通过CLI执行的方式...等等。由于Node基本是不设防的运行,且这些脚本操作的范围相当广,有Fs/Blob等一系列高权限的API,那么攻击开发者是一件相当容易的事情。很多人不会去检查自己的Global文件夹下面的任何代码。同时,大量的包导致这种攻击更难以被发现。
个人认为,现在很难有能够检测这种攻击向量的方式。我提出一种检测方式是通过遍历包的所有源代码,首先检测所有敏感类API给出提醒。然后检测包的消息熵值——避免简单的encoding/aes-hashing绕过,对高于阈值的包给出响应的提醒。最后是通过高强度的,彻底的安全审查,尤其是对于依赖深度过深的包。
同时,这次事件给予所有前端开发者一个警示:不要过度依赖任何库,纯浏览器的解决方案正在完善,我们不能放弃任何的“纯手写”的解决方案。现在的一些原来比较难以实现的——比如表单正则验证,已经可以通过<input pattern>
做到。而且:valid
选择器也让我们可以有进一步的操作空间。
另外一种思路就是,我们可以不添加那些单个的,没有明显作用的依赖包,尤其是对于重要的项目来说。个人认为其实DRY文化在安全之下。对于实在不能抛弃的库,与其使用npm install的方式,不如手动打开源代码,编译并自行检查代码内容,或者参考源代码自己重新实现——这也是AWS的解决方案。