当模板或数据上下文不可信时,启用DoS预防选项至关重要。LiquidJS 提供了三个选项用于此目的:parseLimit、renderLimit 和 memoryLimit。
TL;DR
设置这些选项可以在很大程度上确保你的 LiquidJS 实例不会长时间挂起或消耗过多内存。这些限制基于可用的 JavaScript API,因此它们不是精确的硬性限制,而是确保你的进程不会失败或挂起的阈值。
-const liquid = new Liquid({
- parseLimit: 1e8, // 每次渲染的模板的典型大小
- renderLimit: 1000, // 每次渲染最多 1s
- memoryLimit: 1e9, // LiquidJS 可用的内存(1e9 对应 1GB)
-})
-
-parseLimit
parseLimit 限制每次 .parse() 调用中解析的模板大小(字符长度),包括引用的 partials 和 layouts。由于 LiquidJS 解析模板字符串的时间复杂度接近 O(n),限制模板总长度通常就足够了。
LiquidJS 提供了面向 DoS 的限制选项(parseLimit、renderLimit、memoryLimit)来降低风险。本文按统一结构说明每个限制的作用范围,以及你在生产环境应采用的安全边界。
安全边界
内置限制是协作式防护,不是严格的运行时隔离。
+-
+
- 它不等于进程的 RSS/heap 实际占用。 +
- 它不是 JavaScript 沙箱。 +
- 在生产环境中应结合进程/容器资源限制和请求超时做分层防护。 +
限制速览
-
+
- parseLimit:限制每次
parse()的模板总长度。
+ - renderLimit:限制每次
render()的总渲染时间。
+ - memoryLimit:协作式限制 LiquidJS 已记账的内存敏感分配。 +
限制详解
parseLimit
parseLimit 限制每次 .parse() 调用中解析的模板大小(字符长度),包括引用的 partials 和 layouts。由于 LiquidJS 解析模板字符串的时间复杂度接近 O(n),限制模板总长度通常就足够了。
普通电脑可以很容易处理 1e8(100M)个字符的模板。
renderLimit
仅限制模板大小是不够的,因为在渲染时可能会出现动态的数组和循环。renderLimit 通过限制每次 render() 调用的时间来缓解这些问题。
renderLimit
仅限制模板大小是不够的,因为在渲染时可能会出现动态的数组和循环。renderLimit 通过限制每次 render() 调用的时间来缓解这些问题。
{%- for i in (1..10000000) -%}
order: {{i}}
{%- endfor -%}
渲染时间是在渲染每个模板之前检查的。在上面的例子中,循环中有两个模板:order: 和 {{i}},因此会检查 2x10000000 次。
单个模板内的标签和过滤器仍然可能把进程挂起。要完全控制渲染过程,建议使用类似 paralleljs 的进程管理器。
-memoryLimit
即使模板和迭代次数较少,内存使用量也可能呈指数增长。在下面的示例中,内存会在每次迭代中翻倍:
+renderLimit 不是硬性的 CPU 限制器。它是在模板渲染边界做检查,因此检查点之间的高计算开销过滤器/标签/用户自定义函数,或深层模板嵌套,仍可能导致 DoS。
memoryLimit
memoryLimit 只限制 LiquidJS 显式记账到的操作。
-
+
- 会被统计:LiquidJS 内部调用了内存记账逻辑的内存敏感操作。 +
- 不保证被统计:任意用户对象行为(例如自定义
toValue()/toString()链)以及其他发生在 LiquidJS 记账点之外的宿主侧分配。
+
换句话说,memoryLimit 限制的是 LiquidJS 的“已记账分配”,而不是进程里每一个字节的分配。
即使模板和迭代次数较少,内存使用量也可能呈指数增长。在下面的示例中,内存会在每次迭代中翻倍:
{% assign array = "1,2,3" | split: "," %}
{% for i in (1..32) %}
{% assign array = array | concat: array %}
{% endfor %}
-memoryLimit 限制内存敏感的过滤器,以防止过度的内存分配。由于 JavaScript 使用 GC 来管理内存,memoryLimit 仅限制 LiquidJS 中内存敏感过滤器分配的对象总数,因此可能无法反映实际的内存占用。
由于 JavaScript 使用 GC 来管理内存,memoryLimit 可能无法反映实际的内存占用。
在线服务建议
如果你运行在线服务,建议尽量避免渲染完全由用户定义的模板。
+-
+
- 优先使用受控模板或受限模板子集。 +
- 如果必须支持用户自定义模板,请隔离渲染(worker/进程/容器),并同时配置操作系统或容器级的内存/CPU 限额与请求限流。 +
- 将
parseLimit/renderLimit/memoryLimit视为 DoS 防护体系中的一层,而不是唯一防线。
+
对于单个模板中的重型操作,仍建议使用进程级隔离(例如 paralleljs)。