Skip to content

Security: WebAssembly-cn/design

Security

Security.md

安全

WebAssembly 的安全模型有两个重要目标:(1)防止用户错误或恶意模块;(2)在(1)的约束范围内,为开发安全的应用程序提供开发商有用的原语和缓解措施。

用户

每个 WebAssembly 模块使用故障隔离技术在与主机运行时分离的沙箱环境中执行。这意味着:

  • 应用程序独立执行,并且不能脱离沙箱 浏览适当的 API.
  • 应用程序通常确定性地执行 只有有限的例外

此外,每个模块都受其嵌入的安全策略的约束。在 A网络浏览器中,这包括对信息流通[same-origin policy][]的限制。在non-web平台上,这可能包括 POSIX 安全模型。

开发商

WebAssembly 的设计通过消除其执行语义中的危险功能来促进安全程序,同时保持与为编写C/C++的程序的兼容性。

模块必须在加载时声明所有可访问的函数及其关联类型,即使动态链接在使用时也是如此。这允许通过结构化控制流隐式实施[control-flow integrity][](CFI)。由于编译后的代码是不可变的,并且在运行时无法观察,因此 WebAssembly 程序可以免受控制流劫持攻击。

  • 函数调用必须指定目标的索引 与或表索引空间中的函数索引空间有效条目对应的。
  • 间接函数调用受制于一种类型 运行时签名检查;所选间接函数的类型签名必须与在调用位置指定的类型签名匹配。
  • 中不会发生缓冲区溢出的受保护调用堆栈。 模块堆确保安全的函数返回。
  • Branches必须指向有效 封闭函数内的目的地。

C/C++ 中的变量可以在 WebAssembly 中降低为两个不同的原语,具体取决于它们的作用域。局部变量具有固定范围,并全局变量表示为由索引存储的固定类型值。默认情况下,前者初始化为零,并存储在受保护的调用堆栈中,而后者位于中全局索引空间,可以从外部模块导入。具有的静态范围不明确局部变量(例如,由 address-of 运算符使用,或者为类型 struct 并由值返回)在编译时存储在中单独的用户可寻址堆栈线性存储器中。这是一个具有固定最大大小的独立内存区域,默认情况下初始化为零。以无限精度计算对此内存的引用,以避免换行并简化边界检查。将来,将实现对多个线性存储部分更精细的内存操作(例如,共享内存、页面保护、大页面等)的支持。

Traps用于立即终止执行并向执行环境发出异常行为的信号。在浏览器中,这表示为 JavaScript 异常。未来将实施对的模块定义的陷阱处理程序支持。可以捕获的操作包括:

  • 在任何索引空间中指定无效索引,
  • 使用不匹配的签名执行间接函数调用,
  • 超过受保护调用堆栈的最大大小,
  • 访问出界线性中的地址 存储器,
  • 执行非法算术运算(例如除法或余数 零、有符号除法溢出等)。

内存安全

与传统的 C/C++ 程序相比,这些语义避免了 WebAssembly 中某些类型的内存安全错误。缓冲区溢出发生在数据超出对象边界并访问相邻内存区域时,它不会影响存储在索引空间中的本地或全局变量,它们的大小固定并由索引寻址。存储在线性内存中的数据可以覆盖相邻的对象,因为边界检查是以线性内存区域粒度执行的,并且不是上下文相关的。然而,控制流完整性和受保护的调用堆栈的存在防止了直接的代码注入攻击。因此,WebAssembly 程序不需要[data execution prevention][](DEP)和[stack smashing protection][](SSP)等常见缓解措施。

另一类常见的内存安全错误涉及不安全的指针使用和未定义的行为。这包括取消引用指向未分配内存(例如 NULL)或释放内存分配的指针。在 WebAssembly 中,对于具有固定静态范围的函数调用和变量,指针的语义已被消除,从而允许对任何索引空间中的无效索引的引用在加载时触发验证错误,或者在最坏的情况下在运行时触发陷阱。对线性内存的访问是在区域级别进行边界检查的,这可能会在运行时导致陷阱。这些内存区域与运行时的内部内存隔离,并且默认设置为零,除非以其他方式初始化。

然而,WebAssembly 的语义并不能避免其他类型的错误。虽然攻击者无法执行直接代码注入攻击,但可以使用针对间接调用的代码重用攻击来劫持模块的控制流。然而,使用短指令序列(“小工具”)的常规[return-oriented programming][](ROP)攻击在 WebAssembly 中是不可能的,因为控制流完整性确保调用目标是在加载时声明的有效函数。同样,WebAssembly 中也可能出现竞争条件,例如[time of check to time of use][](TOCTOU)漏洞,因为除了按顺序执行和 [后 MVP 原子内存原语:unicorn:][未来线程] 之外,没有提供执行或调度保证。类似地,[side channel attacks][]也可能发生,例如针对模块的计时攻击。将来,运行时或工具链可能会提供额外的保护,例如代码多样化或内存随机化(类似于address space layout randomization(ASLR))或bounded pointers(“胖”指针)。

控制流完整性

控制流完整性的有效性可以根据其完整性来衡量。通常,有三种类型的外部控制流转换需要保护,因为被调用者可能不受信任:

  1. 直接函数调用,
  2. 间接函数调用,
  3. 回报。

(1)和(2)一起通常被称为“前沿”,因为它们对应于有向控制流图中的前沿。同样,(3)通常被称为“后沿”,因为它对应于有向控制流图中的后沿。更专业的函数调用,如尾部调用,可以看作是(1)和(3)的组合。

通常,这是使用运行时插装实现的。在编译期间,编译器生成程序执行的预期控制流图,并在每个调用点插入运行时插装,以验证转换是否安全。根据程序中所有可能的调用目标的集合来构造预期调用目标的集合,将唯一标识符分配给每个集合,并且插装检查当前调用目标是否是预期调用目标集合的成员。如果该检查成功,则允许原始调用继续进行,否则执行失败处理程序,这通常会终止程序。

在 WebAssembly 中,执行语义隐式地保证了(1)通过使用显式函数节索引和(3)通过受保护的调用堆栈的安全性。此外,已经在运行时检查了间接函数调用的类型签名,有效地实现了(2)的粗粒度的基于类型的控制流完整性。所有这些都是在模块中没有显式运行时插装的情况下实现的。然而,正如所讨论的previously,这种保护并不能防止针对间接调用的函数级粒度的代码重用攻击。

Clang/LLVM CFI

Clang/LLVM 编译器基础设施包括细粒度控制流完整性的 [内置实现],该实现已扩展为支持 WebAssembly 目标。它可以在 Clang/LLVM 3.9+ 中使用 [新的 WebAssembly 后端]。

与默认的 WebAssembly 配置相比,启用细粒度的控制流完整性(通过传递 -fsanitize=cfi 给 Emscripten)具有许多优点。这不仅可以更好地防御利用间接函数调用(2)的代码重用攻击,而且还可以通过在 C/C++ 类型级别进行操作来增强内置函数签名检查,这在语义上比 WebAssemblytype level更丰富,后者仅由四个值类型组成。目前,对于每个间接调用,启用此功能的性能开销很小,因为使用整数范围检查来验证目标索引是否可信,但将来通过利用 WebAssembly 中对 WITH 同构类型的内置支持多个间接表,将会消除这种情况。

[built-in implementation]: http://clang.llvm.org/docs/ControlFlowIntegrity.html [control-flow integrity]: https://research.microsoft.com/apps/pubs/default.aspx?id=64250 [data execution prevention]: https://en.wikipedia.org/wiki/Executable_space_protection [forward-edge control-flow integrity]: https://www.usenix.org/node/184460 [new WebAssembly backend]: https://github.com/WebAssembly/binaryen#cc-source--webassembly-llvm-backend--s2wasm--webassembly [return-oriented programming]: https://en.wikipedia.org/wiki/Return-oriented_programming [same-origin policy]: https://www.w3.org/Security/wiki/Same_Origin_Policy [side channel attacks]: https://en.wikipedia.org/wiki/Side-channel_attack [stack smashing protection]: https://en.wikipedia.org/wiki/Buffer_overflow_protection#Random_canaries [time of check to time of use]: https://en.wikipedia.org/wiki/Time_of_check_to_time_of_use

There aren’t any published security advisories