写点什么

如何安全的运行第三方 JavaScript 代码(上)?

  • 2019-09-18
  • 本文字数:3817 字

    阅读完需:约 13 分钟

如何安全的运行第三方JavaScript代码(上)?

在本文中,我们将为读者详细介绍如何在自己的软件中安全地运行第三方 JavaScript 代码。



最近,我们团队完成了 Figma 插件 API 的开发工作,这样第三方开发人员就可以直接在基于浏览器的设计工具中运行代码。这为第三方开发人员带来便利的同时,也给我们带来许多严峻挑战,比如,如何确保插件中运行的代码不会带来安全问题?


让人更头痛的是,我们的软件是建立在非常规的堆栈之上,因此面临许多工具所没有的约束。我们的设计编辑器是建立在WebGLWebAssembly的基础之上的,其中一些用户界面是利用 Typescript&React 来实现的。并且,我们的软件支持多人同时编辑文件。在这个过程中,浏览器技术为我们提供了很大的支持,同时,也带来了许多的限制。


这篇文章将带你了解我们对完美插件解决方案的探索过程。最终,我们的问题可以归结为一点:如何安全、稳定和高效地运行插件?以下是我们面临的重要约束的简要概述:


1、安全性:插件只有在显示启动时才能访问文件。插件应该被限制在当前文件中。插件不能像 figma.com 那样进行调用。插件不能访问对方的数据,除非是自愿提供的。插件不能篡改 Figma UI 及其行为来误导用户(例如网络钓鱼)。


2、稳定性:插件不能降低 Figma 的速度,使其无法使用。插件不能破坏我们产品中的关键不变量,比如让每个人在查看同一个文件时总是看到相同内容的属性。为了查看文件,不需要管理跨设备/用户的插件安装。对 Figma 产品或内部 API 的修改不会破坏现有的插件。


3、易于开发:插件应该易于开发,以支持充满活力的生态系统。我们的大多数用户都是设计师,可能对 JavaScript 经验不多。开发人员应该能够使用现有的调试工具。


4、性能:插件应该运行得足够快,以支持大多数常见的场景,例如搜索文档、生成图表等等。


尝试 #1:沙箱方法


在我们最初几周的研究工作中,我们尝试了多种第三方代码沙箱,其中一些使用了诸如代码到代码间转换的技术。然而,大多数沙箱都没有在应用程序产品中经过长时间的历练,因此,使用这些沙箱肯定存在一定的风险。


最后,作为我们的第一次尝试,我们使用了最接近标准沙箱解决方案的一种方法:标签。该方法适用于需要运行第三方代码的应用程序,如 CodePen。


需要注意的是,这里的并不是我们平常使用的 HTML 标签。要理解方法为什么能够提供安全性,就必须先来了解一下它提供了哪些特性。


一般来说,通常用于将一个网站嵌入到另一个网站中。例如,在下图中,你可以看到Yelp.com网站中嵌入了Google.com/Maps,这样就可以为用户提供地图功能。



在这里,我们当然不希望因 Yelp 嵌入谷歌地图功能就能读取 Google 网站的内容,因为那里可能存有用户的私人信息。同样,你也不希望谷歌因此而获得了访问 Yelp 网站的内容权限。


这意味着之间的通信应该受到浏览器的严格限制。当的来源与其容器(如yelp.com与google.com)不同时,它们应该是完全隔离的。同时,与进行通信的唯一方法是通过消息传递。实际上,这些消息就是一些纯字符串。收到消息后,每个网站都可以对这些消息采取相应的行动,也可以对它们置之不理。


事实上,它们是如此独立,以至于 HTML 规范允许浏览器将列为单独进程,只要他们喜欢的话。


既然了解了的工作原理,我们就可以通过在每次插件运行时创建一个新的,并将插件的代码粘贴在中来实现插件,这样,插件可以在中做任何想做的事情。但是,除非消息通过了显示的白名单检测,否则,它无法与 Figma 文档进行交互。也是一种特殊的 null 源,这意味着向 figma.com 发送请求的尝试都会被浏览器的跨源资源共享策略所拒绝。



实际上,在这里充当了插件的沙箱角色,而浏览器供应商则为我们提供了沙箱的安全保证,毕竟他们多年来一直在忙着搜索和修复沙箱中的各种漏洞。


使用这个沙箱模型的实际插件将使用我们添加到沙箱中的一个应用程序接口,具体如下所示:


const scene = await figma.loadScene() // gets data from the main threadscene.selection[0].width *= 2scene.createNode({  type: 'RECTANGLE',  x: 10, y: 20,  ...})await figma.updateScene() // flush changes back, to the main thread
复制代码


这里的重点在于,插件是通过调用 loadScene(它向 Figma 发送消息以获取文档的副本)来进行初始化,并通过调用 updateScene(将插件所修改的发送回 Figma)作为其结束的。请注意:


  • 我们是通过获取文档的副本,而不是使用消息传递来完成属性的读取和写入操作。传递消息时,每次往返需耗时 0.1ms,这样的话,每秒只能传递 1000 条左右的消息。

  • 我们不会让插件直接使用 postMessage,因为这样做很麻烦。


决定采用这种方法后,我们大约用了一个月的时间构建好相应的 API。当时来看,马上就大功告成了,我们甚至邀请了一些 alpha 测试人员。然而,我们很快就发现,这种方法存在两大缺陷。


问题 1:async/await 关键字对用户来说不够友好


我们得到的第一反馈是,人们讨厌使用 async/await 关键字——但是在这种方法中,这是不可避免的。消息传递本质上就是异步操作,而在 JavaScript 中是没有办法对异步操作进行同步阻塞式的调用。


对于这种方法,我们不仅需要使用 await 关键字,同时还需要将所有调用函数标签为 async。综上所述,异步/等待仍然是一个比较新颖的 JavaScript 功能,要想玩转它,需要对并发性概念有相当深入的理解——很明显,这对于我们的插件开发人员来说,要求太高。


不过,如果只需要在插件开头和结尾处各使用一次 await 关键字的话,情况就没有那么糟糕。我们只需要告知开发人员始终将 await、loadScene 和 updateScene 搭配使用即可,即使他们不太了解它们的作用,影响也不大。


问题是某些 API 调用需要运行许多复杂的逻辑。例如,有时更改某图层上的单个属性后,必须同时更新其他多个图层。例如,调整 frame 的大小后,需要递归地将约束应用于其子 frame。


这些行为通常涉及许多行为复杂且差别细微的算法。如果因插件而重新实现这些算法的话,肯定不是一个好主意。此外,这些逻辑还会被编译到 WebAssembly 二进制文件中,因此,重用起来并不容易。如果我们不在插件沙箱中运行这些逻辑的话,插件将会读取过时的数据。


所以,尽管这种方法具有一定的可行性,但还是比较麻烦。例如:


await figma.loadScene()... do stuff ...await figma.updateScene()
复制代码


即使是经验丰富的工程师,事情也很快变得非常棘手:


await figma.loadScene()... do stuff ...await figma.updateScene()await figma.loadScene()... do stuff ...await figma.updateScene()await figma.loadScene()... do stuff ...await figma.updateScene()
复制代码


问题 2:场景的复制成本很贵


方法的第二个问题是,在将文档的大部分内容发送到插件之前,需要先对其进行序列化。


事实证明,人们有时会在 Figma 软件中创建非常非常大的文档,甚至达到内存的上限。例如,对于微软的设计系统文件(去年我们花了一个月时间对其进行优化)来说,将文档序列化并将其发送给插件就需要花费 14 秒时间——这些还是发生在插件运行之前。鉴于大多数插件都涉及快速的操作,例如“交换选中的两个对象”,这将使插件的可用性作废。


以增量方式加载数据或延后加载数据也不是一个好的选择,因为:


1.要想重构核心产品,至少需要花上几个月的时间。


2.所有需要等待尚未到达的数据的 API,都是异步的。


总之,因为 Figma 文档可能包含大量相互依赖的数据,所以对我们来说是不可取的。


主线程方法


由于排除了方法,我们不得不另觅他途。此后的两个周,我们又尝试了许多方法,但是或多或少存在某些不可接受的缺陷:


  • API 难以使用(例如,需要使用 REST API 或类似 GraphQL 的方法访问文档)

  • 需要借助浏览器供应商已放弃或正打算放弃的浏览器功能(例如,同步 xhr 请求+Service Worker、共享缓冲区)

  • 需要大量的研究工作或重新构建我们的应用程序,这可能需要花费几个月的时间来验证其可行与否(例如,通过 CRDT,利用 iframe+sync 方式加载 Figma 的副本等)


最终,我们终于得出结论:必须找到一种方法来创建一个模型,其中插件可以直接操作文档。这样,编写插件在一定程度上就是实现手动操作的自动化,为此,我们必须允许插件在主线程上运行。(本文转自嘶吼)


(未完待续)


原文链接:


https://www.4hou.com/technology/20153.html


2019-09-18 16:462665

评论

发布
暂无评论
发现更多内容

国产游戏蓄力,火山引擎ByteHouse助力游戏厂商造爆款

字节跳动数据平台

数据库 大数据 云原生 Clickhouse 数仓

广西北海市等保测评机构有哪些?在哪里?

行云管家

网络安全 北海

正式开拍!快手联合9大导演使用可灵AI拍摄电影;百度文心一言市场负责人张全文否认放弃通用大模型|AI日报

可信AI进展

Apeaksoft Video Fixer for Mac 视频修复软件

Rose

Visio 绘图文件阅读器VSD Viewer for mac

Mac相关知识分享

Mac软件 绘图文件

安装adobe 系列应用出现Error无法继续安装,文件已损坏,无法打开解决方法

理理

Moment for Mac(菜单栏倒数日应用) v1.4激活版

Rose

跨平台键鼠共享工具 synergy for mac 中文版 附synergy最新2024永久密钥

理理

苹果电脑cpu温度监测工具 Turbo Boost Switcher Pro for mac 兼容M芯

理理

XLD无损音频转码 X Lossless Decoder for mac 支持M1/M2

理理

行云管家应邀参加2024腾讯全球数字生态大会, 共创数字生态美好未来

行云管家

云计算 腾讯数字生态大会 数字生态

mac文件隐藏加密 MacPawHider 2 v2.5.1激活版

Rose

iZotope RX 10:重塑音频修复与增强新标准,专业音质触手可及

理理

FTP 客户端软件Transmit for Mac

Mac相关知识分享

ftp

Photoshop 2024 for mac中文激活版

Mac相关知识分享

ps 图像处理软件

苹果电脑专业的SVN管理工具 Cornerstone for Mac永久激活版

理理

交互式UI原型设计神器 Principle for Mac 直装激活版

Rose

SyncBird Pro for Mac(iPhone文件管理器) v4.1.1激活版

Rose

专业数据库管理工具Navicat Premium for Mac

Mac相关知识分享

数据库管理工具

昇思多维混合分布式并行技术,助力伙伴快速复现Llama 3.1-405B大模型

Geek_2d6073

VMware Fusion Pro 13 for Mac(VM虚拟机) v13.5.0中文激活版

Rose

Mac版CAD迷你看图软件都可以做什么?CAD迷你看图破解资源分享

理理

Transmit Mac版FTP客户端提升你在远程服务器上管理文件的效率

理理

交互式原型设计Axure RP 10 for Mac中文激活版

Mac相关知识分享

设计软件

设备管理问题不断,你是否也在为此头疼?

天津汇柏科技有限公司

低代码开发 设备管理 AI人工智能

专访北极星资本:比特币矿业基金背后的努力

TechubNews

XMind for mac (XMind思维导图) v24.01中文永久许可 支持M1/M2/M3

Rose

在Mac电脑和Android设备上进行数据传输:Coolmuster Android Assista

理理

小间距LED屏的特点及发展趋势

Dylan

信息 LED显示屏 全彩LED显示屏 led显示屏厂家 户内led显示屏

VSD Viewer v6.16.1 mac Visio绘图文件阅读器

理理

ON1 Photo RAW MAX 2024.5 功能强大的AI照片编辑软件

理理

如何安全的运行第三方JavaScript代码(上)?_安全_Rudi Chen_InfoQ精选文章