如何在使用 HTML Purifier 处理的内容内的 href 属性中允许大括号

问题描述

我的 CMS 中有一个 redactor 类型字段(我使用 Craft CMS),用户可以在其中输入一些“变量”,如下所示:

“你好,{name}”

唯一的问题是,当启用 HTML 净化器时,它会去除 href 属性中的所有此类“变量”并用代码替换它们。 例如:

<a href="tel:{client tel}">{client tel}</a>

变成

<a href="tel:7207">{client tel}</a>

我当然可以禁用 HTML 净化器,但我宁愿不这样做。我只是很难找到正确的净化器配置来允许所需的行为。有人可以帮忙吗?

解决方法

这个具体的例子是两个过滤器串联应用的结果。第一个是 percent-encoding the "path" portion of the value - 属性值中 tel 方案之后的所有内容,结果为 tel:%7Bclient%20tel%7D。第二个是特定于 tel: URL 方案的过滤器,根据评论,它“从电话号码中删除所有非数字字符、非 x 字符,除了前导加号。” - 这给你留下了 tel:7207

来自HTMLPurifier_URIScheme_tel->doValidate

// Delete all non-numeric characters,non-x characters
// from phone number,EXCEPT for a leading plus sign.
$uri->path = preg_replace('/(?!^\+)[^\dx]/','',// Normalize e(x)tension to lower-case
    str_replace('X','x',$uri->path));

所以这真的是两个问题,第一个是大括号的 URL 编码,第二个是 tel:scheme 中的正则表达式。

解决此问题的简单方法是指示 HTMLPurifier 将 href 标记上的 a 属性评估为文本而不是 URI。 URI 评估非常严格,这是应该的。由于您需要通过过滤器传递无效的 URI,您可以使用默认文本过滤,或创建您自己的特定于您的需要的过滤器。我将在这里描述前者,后者是一个更复杂的练习。

请注意,这将导致 HTMLPurifier 将所有 a href 属性评估为文本,您将失去对所有链接的严格验证 - 请确保您了解对应用程序安全性的潜在影响。

$config = HTMLPurifier_Config::createDefault();
$config->set('HTML.DefinitionID','trusted');

if ($def = $config->maybeGetRawHTMLDefinition())
{
    $def->addAttribute('a','href','Text');
}

$purifier = new HTMLPurifier($config);

有关详细信息,请参阅 customizing 文档。

这里的主要危险是您正在删除 javascript: 方案的过滤。文本过滤器将转义脚本标签,但不会过滤方案中的内联命令。

输入:

<a href="<script>alert(1)</script>">Script tag in href with alert</a>
<a href="javascript:alert(1)">javascript scheme with alert</a>

默认转义:

<a href="">Script tag in href with alert</a>
<a>javascript scheme with alert</a>

文本转义:

<a href="&lt;script&gt;alert(1)&lt;/script&gt;">Script tag in href with alert</a>
<a href="javascript:alert(1)">javascript scheme with alert</a>

当我做这样的事情时,我使用两种不同的定义,一种称为“可信”,用于过滤来自可信来源的内容,例如应该知道自己在做什么的 CMS 管理员,另一种称为“偏执”,用于来自不受信任来源的内容。

另一种降低风险的策略是在将内容输入 CMS 时允许这种许可性转义(可信定义),然后在内容呈现后应用更严格的过滤(偏执定义)。无论如何,在输出时转义是一个很好的做法,以防止 stored xss attacks