非常精彩的一篇文章,作者是 Cloudflare Workers 团队的一名安全工程师,讲了 Workers 在安全方面的设计。
安全第一步就是隔离,先是用 V8 的 Isolates 来实现进程内隔离: 这些 workers 代码运行在一个进程内,但只能访问被分配的资源,是一个更轻量级的容器实现。好处就是可以在一台机器上部署很多 worker,并且可以快速初始化运行环境。然后再用 Linux 的 namespace 和 seccomp 来进一步资源隔离和禁用系统调用。比如为新 worker 创建一个空的文件系统(挂在特定 namespace 下),然后通过 seccomp 禁用所有文件相关的系统调用,同时也会禁用网络请求,所有跟外部的请求都要通过一个中介来完成。
完成了资源分配和禁用系统调用后,接下来就要设计 API,让 worker 可以使用文件操作、网络请求等常见能力了。这里要进行抽象,在 Worker 看来就像正常操作文件,请求 URL(有点像 Duck Typing),这就涉及到 API 的设计,如果设计成同步调用就会产生多个 Round Trip,就有可能产生性能问题。这里他们用了 Cap'n Proto RPC,简单来讲就是用 Promise
来替代真正的结果,然后调用方通过 await
去拿最后的数据,除此之外还支持 pipeline
同步和异步在 API 设计上是完全不一样的,所以底层的实现要在一开始就想清楚。
然后就来到了 Spectre 防护。关于 Spectre and Meltdown attack 可以参见这个视频,讲得很清楚。核心要点是通过 CPU 在执行特定运算时透露出来的信息来反推目标信息,而这个「特定运算」可以借助 CPU 的预执行优化(把数据提前放到 CPU Cache 里)来达到,在预执行阶段 CPU 会跳过一些防护(如数组越界判断)直接执行(因为它认为之后真正执行时内核会去防护)代码,比如把这段代码包在一个恒为 false 的条件判断里, CPU 就会在预知性阶段运行这些代码。
业界没有从根上解决 Spectre 攻击的方案,只能不断打补丁。但这个太被动了,所以 cloudflare 又做了一些事情,让 Spectre 攻击几乎不可能,这个思路还是挺有意思的。
Date.now()
都会返回同一个值,这样就无法得到时间差。Worker 的设计还是很有挑战的,既要保持高效(快速启动实例,响应请求),又要保证安全,同时能力也要跟上,满足客户需求,如何设计和权衡是一门艺术。
cloudfare 推出的一个用户友好,保护隐私的验证码方案(还处于 beta 阶段)。我们经常会在网上看到让你选择包含汽车或巴士的验证码,这些大多是 Google 提供的验证码方案,不但对用户不友好,还会搜集用户的一些隐私数据。cloudflare 新推出的 Turnstile 服务,可以不展示交互内容,90% 的用户都不会感知到验证码的存在,当 Tunstile 收集的数据和运算之后无法确定是真实用户时,才会显示验证码。具体可以参见这篇文章。它的执行流程如下:
When a visitor encounters a Managed Challenge, we first run a series of small non-interactive JavaScript challenges gathering more signals about the visitor/browser environment. This means we deploy in-browser detections and challenges at the time the request is made. Challenges are selected based on what characteristics the visitor emits and based on the initial information we have about the visitor. Those challenges include, but are not limited to, proof-of-work, proof-of-space, probing for web APIs, and various challenges for detecting browser-quirks and human behavior.
VS Code 内置的 Timeline 功能结合了本地修改记录和 Git 修改记录,本地修改记录有点像 Snapshot,不需要手动 Commit,能自动快照,因此可以找回未保存的内容,或被删除的文件(未被 Git 管理),这个在特定场景还是挺有用的。
比较细致地介绍了从零搭建一套个人网站需要的技术、工具和服务,对于一个不是从事软件开发的同学来说,要了解的东西还是有点多。
中文的翻译可以看这里。在云服务大行其道的当下,数据相关的问题开始也开始受到重视。如果在创作某样东西时投入了大量的精力,就会有很深的情感,自然会希望可以随时打开它,持续创作,但云服务无法保证这点。有太多可能出状况的地方了:服务出现问题导致无法访问或丢失或泄漏、合规问题、网络问题和隐私问题,还可能调整定价策略。当然像 Google 这样的大公司技术层面是有保障的,但由于依赖网络,体验上跟本地还是会有些差别,而且「网络不好就几乎不能用」这也是个限制。
Local First 不仅是数据存在本地,而且是优先使用本地数据,没有网络也可以正常使用,隐私也有保障。但随之而来的问题是不方便协同和跨平台(其实就是云服务的优势)。CRDT 技术一定程度上让 Local First 的协同变得可能,但依旧很难(想象 Git 的冲突)。所以我觉得这类场景不如放弃协同和跨平台,比如 Sketch。需要协同和跨平台的就放弃 Local First。
这篇文章从 7 个维度深度分析了 Local First 和云服务之间各自的优劣,以及在 Local First 软件如何解决协作的问题。
Unfortunately, cloud apps are problematic in this regard. Although they let you access your data anywhere, all data access must go via the server, and you can only do the things that the server will let you do. In a sense, you don’t have full ownership of that data — the cloud provider does. In the words of a bumper sticker: "There is no cloud, it’s just someone else’s computer."
"Old-fashioned" apps continue to work forever, as long as you have a copy of the data and some way of running the software. Even if the software author goes bust, you can continue running the last released version of the software. Even if the operating system and the computer it runs on become obsolete, you can still run the software in a virtual machine or emulator. As storage media evolve over the decades, you can copy your files to new storage media and continue to access them.
中文翻译可以看这里,也可以看下作者翻译的另一篇文章作为铺垫。
本文的核心是让局域网里的两台电脑互相通信,因为都是在局域网所以无法直接通过公网 IP 来通信,这里就涉及到突破防火墙和 NAT。有一个思路很巧妙,大部分防火墙都设置为宽出严进(如可以请求外部链接,但拒绝外部设备的访问请求),而这个机制的核心是判断最近的出去记录上是否有目标地址,如果有表示这个是受信任的地址,也不管出去的请求是否真的到了这个地址。通过 STUN 服务器拿到两台设备的外网 IP 和端口后,就可以利用这个「协议漏洞」来建立连接了。
正如 80/20
定律一样,20% 的场景可能要花 80% 的功夫才能搞定,有一些 NAT 场景(hard NAT)即使是同一个 Socket 连接,访问不同的 IP 和端口,会在 NAT 设备上产生不同的 PORT,然后就要结合端口扫描来找到正确的 PORT,其中又会涉及到一些技巧来避免被判定为在扫描端口而加入黑名单,等等。
通过这篇文章可以对 NAT 和突破 NAT / 防火墙的限制,让局域网的机器互联有更多的了解。
👍