安全

htmx 允许你直接在 DOM 中定义逻辑。这有许多优点,其中最大的优点是 行为局部性,它使你的系统更易于理解和维护。

然而,这种方法的一个问题是安全性:由于 htmx 增加了 HTML 的表现力,如果恶意用户能够将 HTML 注入到你的应用程序中,他们就可以利用 htmx 的这种表现力来达到恶意目的。

规则 1:转义所有用户内容

基于 HTML 的 Web 开发的第一条规则一直是:不要信任用户的输入。你应该避开注入到你网站的所有第三方、不受信任的内容。这是为了防止 XSS 攻击等问题。

优秀的 OWASP 网站上有大量关于 XSS 及其预防方法的文档,包括跨站点脚本预防备忘单。

好消息是,这是一个非常古老且易于理解的话题,绝大多数服务器端模板语言都支持自动转义内容以防止出现此类问题。

话虽如此,有时人们会选择更危险地注入 HTML,通常是通过 raw() 模板语言中的某种机制。这样做可以有充分的理由,但如果注入的内容来自第三方,则必须hx-对其进行清理,包括删除以和开头的属性 data-hx 以及内联 <script> 标签等。

如果你正在注入原始 HTML 并进行自己的转义,最佳做法是将你允许的属性和标签列入白名单,而不是将你不允许的属性和标签列入黑名单。

htmx 安全工具

当然,错误会发生,开发人员也不是完美的,因此最好对你的 Web 应用程序采用分层的安全方法,并且 htmx 提供了工具来帮助保护你的应用程序。

让我们来看看它们。

hx-disable

htmx 提供的第一个帮助进一步保护应用程序的工具是hx-disable 属性。此属性将阻止处理给定元素上的所有 htmx 属性,以及其中的所有元素。因此,例如,如果你在模板中包含原始 HTML 内容(再次强调,不建议这样做!),那么你可以在内容周围放置一个带有属性的 div hx-disable:

<div hx-disable>
    <%= raw(user_content) %>
</div>

并且 htmx 不会处理在该内容中找到的任何与 htmx 相关的属性或功能。此属性无法通过注入更多内容来禁用:如果 hx-disable 在元素的父级层次结构中的任何位置找到某个属性,则 htmx 不会处理该属性。

hx-history

另一个安全考虑因素是 htmx 历史缓存。你可能有一些页面包含敏感数据,而你不想将这些数据存储在用户缓存中。你可以通过在页面的任何位置 localStorage 包含该属性并将其值设置为 来从历史缓存中忽略给定页面 。hx-history false

配置选项

htmx 还提供了与安全相关的配置选项:

  • htmx.config.selfRequestsOnly - 如果设置为true,则只允许对与当前文档相同的域的请求
  • htmx.config.allowScriptTags - htmx 将处理 <script> 在其加载的新内容中找到的标签。如果你希望禁用此行为,可以将此配置变量设置为 false
  • htmx.config.historyCacheSize - 可以设置为避免在缓存0中存储任何 HTMLlocalStorage
  • htmx.config.allowEval - 可以设置为 false 禁用所有依赖于 eval 的 htmx 功能:
    • 事件过滤器
    • hx-on:属性
    • hx-vals带有js:前缀
    • hx-headers带有js:前缀

请注意,所有通过禁用删除的功能都 eval() 可以使用你自己的自定义 javascript 和 htmx 事件模型重新实现。

事件

如果你希望允许对当前主机之外的某些域的请求,但又不完全开放,则可以使用事件 htmx:validateUrl 。此事件将在槽中提供请求 URLdetail.url 以及 sameHost 属性。

你可以检查这些值,如果请求无效,则调用 preventDefault() 事件以阻止发出请求。

document.body.addEventListener('htmx:validateUrl', function (evt) {
  // only allow requests to the current server as well as myserver.com
  if (!evt.detail.sameHost && evt.detail.url.hostname !== "myserver.com") {
    evt.preventDefault();
  }
});

CSP 选项

浏览器还提供进一步保护 Web 应用程序的工具。最强大的工具是 内容安全策略。使用 CSP,你可以告诉浏览器不要向非源主机发出请求、不要评估内联脚本标签等。

以下是标签中的 CSP 示例meta:

    <meta http-equiv="Content-Security-Policy" content="default-src 'self';">

这告诉浏览器“仅允许连接到原始(源)域”。这与 是多余的 htmx.config.selfRequestsOnly ,但在处理应用程序安全性时,分层的安全方法是必要的,事实上,也是理想的。

对 CSP 的完整讨论超出了本文档的范围,但MDN 文章为探索该主题提供了一个很好的起点。