Files
liquidjs/zh-cn/tutorials/security-model.html
T

219 lines
21 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-cn">
<head prefix="og: https://ogp.me/ns#">
<meta charset="utf-8">
<title>安全模型 | LiquidJS</title>
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="LiquidJS is a simple, expressive and safe Shopify / GitHub Pages compatible template engine in pure JavaScript.">
<link rel="dns-prefetch" href="https://cdn.jsdelivr.net/">
<link rel="manifest" href="/zh-cn/manifest.json">
<!-- Canonical links -->
<link rel="canonical" href="https://liquidjs.com/zh-cn/tutorials/security-model.html">
<!-- Alternative links -->
<link rel="alternative" hreflang="en" href="https://liquidjs.com/zh-cn/tutorials/security-model">
<link rel="alternative" hreflang="zh-cn" href="https://liquidjs.com/zh-cn/zh-cn/tutorials/security-model">
<!-- Icon -->
<link rel="apple-touch-icon" sizes="57x57" href="../../icon/apple-touch-icon-57x57.png">
<link rel="apple-touch-icon" sizes="114x114" href="../../icon/apple-touch-icon-114x114.png">
<link rel="apple-touch-icon" sizes="72x72" href="../../icon/apple-touch-icon-72x72.png">
<link rel="apple-touch-icon" sizes="144x144" href="../../icon/apple-touch-icon-144x144.png">
<link rel="apple-touch-icon" sizes="60x60" href="../../icon/apple-touch-icon-60x60.png">
<link rel="apple-touch-icon" sizes="120x120" href="../../icon/apple-touch-icon-120x120.png">
<link rel="apple-touch-icon" sizes="76x76" href="../../icon/apple-touch-icon-76x76.png">
<link rel="apple-touch-icon" sizes="152x152" href="../../icon/apple-touch-icon-152x152.png">
<link rel="icon" type="image/png" href="../../icon/favicon-196x196.png" sizes="196x196">
<link rel="icon" type="image/png" href="../../icon/favicon-160x160.png" sizes="160x160">
<link rel="icon" type="image/png" href="../../icon/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="../../icon/favicon-16x16.png" sizes="16x16">
<link rel="icon" type="image/png" href="../../icon/favicon-32x32.png" sizes="32x32">
<meta name="msapplication-TileColor" content="#2f83cd">
<meta name="msapplication-TileImage" content="../../icon/mstile-144x144.png">
<link rel="stylesheet" href="../../css/navy.css">
<link rel="alternate" href="../../atom.xml" title="LiquidJS" type="application/atom+xml">
<meta name="generator" content="Hexo 7.3.0"></head>
<body>
<div id="container">
<header id="header" class="wrapper">
<div id="header-inner" class="inner">
<h1 id="logo-wrap">
<a href="/zh-cn/" id="logo">LiquidJS</a>
</h1>
<nav id="main-nav">
<a href="intro-to-liquid.html" class="main-nav-link">教程</a><a href="../tags/overview.html" class="main-nav-link">标签</a><a href="../filters/overview.html" class="main-nav-link">过滤器</a><a href="../playground.html" class="main-nav-link">演示</a><a href="../../api/classes/Liquid.html" class="main-nav-link">API</a>
<div id="search-input-wrap">
<i id="search-input-icon" class="icon-search"></i>
<input type="search" id="search-input" placeholder="搜索...">
</div>
</nav>
<div class="main-nav-link icon-nav-link">
<label><i class="icon-network"></i> <span class="icon-nav-title">简体中文</span></label>
<select id="lang-select" data-canonical="tutorials/security-model.html">
<option value="en">English</option>
<option value="zh-cn" selected>简体中文</option>
</select>
</div>
<a target="_blank" rel="noopener external nofollow noreferrer" href="https://github.com/harttle/liquidjs" class="main-nav-link icon-nav-link"><i class="icon-github"></i> <span class="icon-nav-title">GitHub</span></a>
<a id="mobile-nav-toggle">
<span class="mobile-nav-toggle-bar"></span>
<span class="mobile-nav-toggle-bar"></span>
<span class="mobile-nav-toggle-bar"></span>
</a>
</div>
</header>
<div id="content-wrap">
<div id="content" class="wrapper">
<div id="content-inner">
<aside id="sidebar" role="navigation">
<div class="inner">
<strong class="sidebar-title">开始使用</strong><a href="intro-to-liquid.html" class="sidebar-link">Liquid 简介</a><a href="setup.html" class="sidebar-link">安装</a><a href="options.html" class="sidebar-link">选项</a><a href="render-file.html" class="sidebar-link">文件渲染</a><a href="partials-and-layouts.html" class="sidebar-link">引用/继承</a><a href="use-in-expressjs.html" class="sidebar-link">Express.js 中使用</a><strong class="sidebar-title">高级主题</strong><a href="caching.html" class="sidebar-link">缓存</a><a href="escaping.html" class="sidebar-link">转义</a><a href="register-filters-tags.html" class="sidebar-link">注册标签/过滤器</a><a href="access-scope-in-filters.html" class="sidebar-link">过滤器里访问上下文</a><a href="parse-parameters.html" class="sidebar-link">参数解析</a><a href="render-tag-content.html" class="sidebar-link">渲染标签内容</a><a href="drops.html" class="sidebar-link">Liquid Drop</a><a href="sync-and-async.html" class="sidebar-link">同步和异步</a><a href="whitespace-control.html" class="sidebar-link">换行和缩进</a><a href="plugins.html" class="sidebar-link">插件</a><a href="operators.html" class="sidebar-link">运算符</a><a href="truthy-and-falsy.html" class="sidebar-link">真和假</a><a href="security-model.html" class="sidebar-link current">安全模型</a><a href="static-analysis.html" class="sidebar-link">静态分析</a><strong class="sidebar-title">其他</strong><a href="migrate-to-9.html" class="sidebar-link">迁移到 LiquidJS 9</a><a href="changelog.html" class="sidebar-link">更新日志</a><a href="differences.html" class="sidebar-link">与 Shopify/liquid 的不同</a><a href="contribution-guidelines.html" class="sidebar-link">贡献指南</a>
</div>
</aside>
<article class="article-container" itemscope itemtype="https://schema.org/Article">
<div class="article-inner">
<div class="article">
<div class="inner">
<header class="article-header">
<h1 class="article-title" itemprop="name">安全模型</h1>
<a target="_blank" rel="noopener external nofollow noreferrer" href="https://github.com/harttle/liquidjs/edit/master/docs/source/zh-cn/tutorials/security-model.md" class="article-edit-link" title="改进这篇文档"><i class="icon-pencil"></i></a>
</header>
<div class="article-content" itemprop="articleBody">
<p>LiquidJS 提供了面向 DoS 的限制选项(<code>parseLimit</code><code>renderLimit</code><code>memoryLimit</code>)来降低风险。本文按统一结构说明每个限制的作用范围,以及你在生产环境应采用的安全边界。</p>
<h2 id="安全边界" class="article-heading"><a href="#安全边界" class="headerlink" title="安全边界"></a>安全边界<a class="article-anchor" href="#安全边界" aria-hidden="true"></a></h2><p>内置限制是协作式防护,不是严格的运行时隔离。</p>
<ul>
<li><strong>不等于</strong>进程的 RSS/heap 实际占用。</li>
<li><strong>不是</strong> JavaScript 沙箱。</li>
<li>在生产环境中应结合进程/容器资源限制和请求超时做分层防护。</li>
</ul>
<h2 id="限制速览" class="article-heading"><a href="#限制速览" class="headerlink" title="限制速览"></a>限制速览<a class="article-anchor" href="#限制速览" aria-hidden="true"></a></h2><ul>
<li><a href="/api/interfaces/LiquidOptions.html#parseLimit">parseLimit</a>:限制每次 <code>parse()</code> 的模板总长度。</li>
<li><a href="/api/interfaces/LiquidOptions.html#renderLimit">renderLimit</a>:限制每次 <code>render()</code> 的总渲染时间。</li>
<li><a href="/api/interfaces/LiquidOptions.html#memoryLimit">memoryLimit</a>:协作式限制 LiquidJS 已记账的内存敏感分配。</li>
</ul>
<h2 id="限制详解" class="article-heading"><a href="#限制详解" class="headerlink" title="限制详解"></a>限制详解<a class="article-anchor" href="#限制详解" aria-hidden="true"></a></h2><h3 id="parseLimit" class="article-heading"><a href="#parseLimit" class="headerlink" title="parseLimit"></a>parseLimit<a class="article-anchor" href="#parseLimit" aria-hidden="true"></a></h3><p><a href="/api/interfaces/LiquidOptions.html#parseLimit">parseLimit</a> 限制每次 <code>.parse()</code> 调用中解析的模板大小(字符长度),包括引用的 partials 和 layouts。由于 LiquidJS 解析模板字符串的时间复杂度接近 O(n),限制模板总长度通常就足够了。</p>
<p>普通电脑可以很容易处理 <code>1e8</code>100M)个字符的模板。</p>
<h3 id="renderLimit" class="article-heading"><a href="#renderLimit" class="headerlink" title="renderLimit"></a>renderLimit<a class="article-anchor" href="#renderLimit" aria-hidden="true"></a></h3><p>仅限制模板大小是不够的,因为在渲染时可能会出现动态的数组和循环。<a href="/api/interfaces/LiquidOptions.html#renderLimit">renderLimit</a> 通过限制每次 <code>render()</code> 调用的时间来缓解这些问题。</p>
<pre class="line-numbers language-liquid" data-language="liquid"><code class="language-liquid"><span class="token liquid language-liquid"><span class="token delimiter punctuation">&#123;%-</span> <span class="token keyword">for</span> i <span class="token keyword">in</span> <span class="token punctuation">(</span><span class="token number">1</span><span class="token range operator">..</span><span class="token number">10000000</span><span class="token punctuation">)</span> <span class="token delimiter punctuation">-%&#125;</span></span>
order: <span class="token liquid language-liquid"><span class="token delimiter punctuation">&#123;&#123;</span>i<span class="token delimiter punctuation">&#125;&#125;</span></span>
<span class="token liquid language-liquid"><span class="token delimiter punctuation">&#123;%-</span> <span class="token keyword">endfor</span> <span class="token delimiter punctuation">-%&#125;</span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre>
<p>渲染时间是在渲染每个模板之前检查的。在上面的例子中,循环中有两个模板:<code>order:</code><code>&#123;&#123;i&#125;&#125;</code>,因此会检查 2x10000000 次。</p>
<p><code>renderLimit</code> 不是硬性的 CPU 限制器。它是在模板渲染边界做检查,因此检查点之间的高计算开销过滤器/标签/用户自定义函数,或深层模板嵌套,仍可能导致 DoS。</p>
<h3 id="memoryLimit" class="article-heading"><a href="#memoryLimit" class="headerlink" title="memoryLimit"></a>memoryLimit<a class="article-anchor" href="#memoryLimit" aria-hidden="true"></a></h3><p><code>memoryLimit</code> 只限制 LiquidJS 显式记账到的操作。</p>
<ul>
<li>会被统计:LiquidJS 内部调用了内存记账逻辑的内存敏感操作。</li>
<li>不保证被统计:任意用户对象行为(例如自定义 <code>toValue()</code> / <code>toString()</code> 链)以及其他发生在 LiquidJS 记账点之外的宿主侧分配。</li>
</ul>
<p>换句话说,<code>memoryLimit</code> 限制的是 LiquidJS 的“已记账分配”,而不是进程里每一个字节的分配。</p>
<p>即使模板和迭代次数较少,内存使用量也可能呈指数增长。在下面的示例中,内存会在每次迭代中翻倍:</p>
<pre class="line-numbers language-liquid" data-language="liquid"><code class="language-liquid"><span class="token liquid language-liquid"><span class="token delimiter punctuation">&#123;%</span> <span class="token keyword">assign</span> array <span class="token operator">=</span> <span class="token string">"1,2,3"</span> <span class="token operator">|</span> <span class="token function filter">split</span><span class="token operator">:</span> <span class="token string">","</span> <span class="token delimiter punctuation">%&#125;</span></span>
<span class="token liquid language-liquid"><span class="token delimiter punctuation">&#123;%</span> <span class="token keyword">for</span> i <span class="token keyword">in</span> <span class="token punctuation">(</span><span class="token number">1</span><span class="token range operator">..</span><span class="token number">32</span><span class="token punctuation">)</span> <span class="token delimiter punctuation">%&#125;</span></span>
<span class="token liquid language-liquid"><span class="token delimiter punctuation">&#123;%</span> <span class="token keyword">assign</span> array <span class="token operator">=</span> array <span class="token operator">|</span> <span class="token function filter">concat</span><span class="token operator">:</span> array <span class="token delimiter punctuation">%&#125;</span></span>
<span class="token liquid language-liquid"><span class="token delimiter punctuation">&#123;%</span> <span class="token keyword">endfor</span> <span class="token delimiter punctuation">%&#125;</span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre>
<p>由于 <a target="_blank" rel="noopener external nofollow noreferrer" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_management">JavaScript 使用 GC 来管理内存</a><code>memoryLimit</code> 可能无法反映实际的内存占用。</p>
<h2 id="在线服务建议" class="article-heading"><a href="#在线服务建议" class="headerlink" title="在线服务建议"></a>在线服务建议<a class="article-anchor" href="#在线服务建议" aria-hidden="true"></a></h2><p>如果你运行在线服务,建议尽量避免渲染完全由用户定义的模板。</p>
<ul>
<li>优先使用受控模板或受限模板子集。</li>
<li>如果必须支持用户自定义模板,请隔离渲染(worker/进程/容器),并同时配置操作系统或容器级的内存/CPU 限额与请求限流。</li>
<li><code>parseLimit</code> / <code>renderLimit</code> / <code>memoryLimit</code> 视为 DoS 防护体系中的一层,而不是唯一防线。</li>
</ul>
<p>对于单个模板中的重型操作,仍建议使用进程级隔离(例如 <a target="_blank" rel="noopener external nofollow noreferrer" href="https://www.npmjs.com/package/paralleljs">paralleljs</a>)。</p>
</div>
<footer class="article-footer">
<time class="article-footer-updated" datetime="2026-05-11T16:00:04.057Z" itemprop="dateModified">上次更新:2026-05-11</time>
<a href="truthy-and-falsy.html" class="article-footer-prev" title="真和假"><i class="icon-chevron-left"></i><span>上一页</span></a><a href="static-analysis.html" class="article-footer-next" title="静态分析"><span>下一页</span><i class="icon-chevron-right"></i></a>
</footer>
</div>
</div>
<aside id="article-toc" role="navigation">
<div id="article-toc-inner">
<div id="article-toc-inner-list">
<strong class="sidebar-title">目录</strong>
<ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%AE%89%E5%85%A8%E8%BE%B9%E7%95%8C"><span class="toc-text">安全边界</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E9%99%90%E5%88%B6%E9%80%9F%E8%A7%88"><span class="toc-text">限制速览</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E9%99%90%E5%88%B6%E8%AF%A6%E8%A7%A3"><span class="toc-text">限制详解</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#parseLimit"><span class="toc-text">parseLimit</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#renderLimit"><span class="toc-text">renderLimit</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#memoryLimit"><span class="toc-text">memoryLimit</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%9C%A8%E7%BA%BF%E6%9C%8D%E5%8A%A1%E5%BB%BA%E8%AE%AE"><span class="toc-text">在线服务建议</span></a></li></ol>
</div>
<a href="#" id="article-toc-top">回到顶部</a>
</div>
</aside>
</div>
</article>
</div>
</div>
</div>
<footer id="footer" class="wrapper">
<div class="inner">
<div id="footer-copyright">
&copy; 2026 <a href="https://github.com/harttle/liquidjs/graphs/contributors" rel="external nofollow noreferrer" target="_blank">Harttle</a><br>
本文档通过 <a href="http://creativecommons.org/licenses/by/4.0/" rel="external nofollow noreferrer" target="_blank">CC BY 4.0</a> 授权。
</div>
<div id="footer-links">
<a href="https://twitter.com/harttleharttle" rel="external nofollow noreferrer" class="footer-link" target="_blank"><i class="icon-twitter"></i></a>
<a href="https://opencollective.com/liquidjs" rel="external nofollow noreferrer" class="footer-link" target="_blank"><i class="icon-opencollective"></i></a>
<a href="https://github.com/harttle/liquidjs" rel="external nofollow noreferrer" class="footer-link" target="_blank"><i class="icon-github"></i></a>
</div>
</div>
</footer>
</div>
<div id="mobile-nav-dimmer"></div>
<nav id="mobile-nav">
<div id="mobile-nav-inner">
<ul id="mobile-nav-list">
<a href="intro-to-liquid.html" class="mobile-nav-link">教程</a><a href="../tags/overview.html" class="mobile-nav-link">标签</a><a href="../filters/overview.html" class="mobile-nav-link">过滤器</a><a href="../playground.html" class="mobile-nav-link">演示</a><a href="../../api/classes/Liquid.html" class="mobile-nav-link">API</a>
</ul>
<div class="mobile-sidebar-list">
<strong class="mobile-nav-title">开始使用</strong><a href="intro-to-liquid.html" class="mobile-nav-link">Liquid 简介</a><a href="setup.html" class="mobile-nav-link">安装</a><a href="options.html" class="mobile-nav-link">选项</a><a href="render-file.html" class="mobile-nav-link">文件渲染</a><a href="partials-and-layouts.html" class="mobile-nav-link">引用/继承</a><a href="use-in-expressjs.html" class="mobile-nav-link">Express.js 中使用</a><strong class="mobile-nav-title">高级主题</strong><a href="caching.html" class="mobile-nav-link">缓存</a><a href="escaping.html" class="mobile-nav-link">转义</a><a href="register-filters-tags.html" class="mobile-nav-link">注册标签/过滤器</a><a href="access-scope-in-filters.html" class="mobile-nav-link">过滤器里访问上下文</a><a href="parse-parameters.html" class="mobile-nav-link">参数解析</a><a href="render-tag-content.html" class="mobile-nav-link">渲染标签内容</a><a href="drops.html" class="mobile-nav-link">Liquid Drop</a><a href="sync-and-async.html" class="mobile-nav-link">同步和异步</a><a href="whitespace-control.html" class="mobile-nav-link">换行和缩进</a><a href="plugins.html" class="mobile-nav-link">插件</a><a href="operators.html" class="mobile-nav-link">运算符</a><a href="truthy-and-falsy.html" class="mobile-nav-link">真和假</a><a href="security-model.html" class="mobile-nav-link current">安全模型</a><a href="static-analysis.html" class="mobile-nav-link">静态分析</a><strong class="mobile-nav-title">其他</strong><a href="migrate-to-9.html" class="mobile-nav-link">迁移到 LiquidJS 9</a><a href="changelog.html" class="mobile-nav-link">更新日志</a><a href="differences.html" class="mobile-nav-link">与 Shopify/liquid 的不同</a><a href="contribution-guidelines.html" class="mobile-nav-link">贡献指南</a>
</div>
</div>
<div id="mobile-button-list">
<a href="https://github.com/harttle/liquidjs" class="mobile-nav-link" rel="external" target="_blank"><i class="icon-github"></i></a>
<a href="https://opencollective.com/liquidjs" class="mobile-nav-link" rel="external" target="_blank"><i class="icon-opencollective"></i></a>
<div id="mobile-lang-select-wrap" class="mobile-nav-link">
<label for="mobile-lang-select"><i class="icon-network"></i></label>
<select id="mobile-lang-select" data-canonical="tutorials/security-model.html">
<option value="en">English</option>
<option value="zh-cn" selected>简体中文</option>
</select>
</div>
</div>
</nav>
<script src="../../js/main.js"></script>
<script src="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.js"></script>
<script>
document.getElementById('search-input-wrap').classList.add('on');
docsearch({
appId: 'QJ35YOZTU4',
apiKey: '8c6cbb824b4c5023f0bb2ef29e228bef',
indexName: 'liquidjs',
inputSelector: '#search-input',
debug: false
});
</script>
</body>
</html>