剖析 `wp_kses()` 函数的源码,解释它如何使用 `_wp_kses_allowed_html` 钩子允许开发者自定义白名单?

早上好,各位观众老爷!今天咱们来聊聊 WordPress 里一个非常重要的函数——wp_kses()。这玩意儿就像个保安,专门负责过滤用户输入,防止恶意代码溜进你的网站。但保安总不能啥都拦着吧?所以 WordPress 提供了 _wp_kses_allowed_html 钩子,让咱们可以自定义白名单,告诉保安哪些 HTML 标签和属性是“良民”,可以放行。

咱们今天就来扒一扒 wp_kses() 的源码,看看它是怎么利用这个钩子来实现自定义白名单的。准备好了吗?发车!

wp_kses():一个 HTML 标签和属性的“洁癖症患者”

首先,我们来认识一下 wp_kses() 这个函数。它的主要作用就是过滤 HTML 标签和属性,只允许白名单上的标签和属性通过,其他的统统咔嚓掉。

简单来说,wp_kses() 就像一个非常严格的 HTML 验证器,它会根据你定义的规则(白名单)来判断哪些 HTML 代码可以保留,哪些需要删除。

_wp_kses_allowed_html 钩子:给保安送“通行证”

_wp_kses_allowed_html 钩子就相当于你给保安送了一份“通行证”名单,告诉他名单上的人(HTML 标签和属性)是你的朋友,可以自由出入。

这个钩子允许开发者修改 wp_kses() 默认的 HTML 白名单。你可以添加新的标签和属性,也可以删除已有的标签和属性,从而定制符合你网站需求的过滤规则。

源码剖析:wp_kses() 是如何工作的?

咱们直接上代码,看看 wp_kses() 的真面目:

function wp_kses( $string, $allowed_html, $allowed_protocols = array() ) {
    // ... (一些初始化和参数处理) ...

    $string = wp_kses_no_null( $string );
    $string = wp_kses_normalize_entities( $string );
    $string = wp_kses_hook( $string, $allowed_html, $allowed_protocols );

    return $string;
}

可以看到,wp_kses() 函数主要做了以下几件事:

  1. 初始化和参数处理: 对输入字符串进行一些预处理,比如去除空字符。
  2. 规范化 HTML 实体: 将 HTML 实体转换为对应的字符。
  3. 调用 wp_kses_hook() 进行核心过滤: 这是最关键的一步,wp_kses_hook() 才是真正执行 HTML 过滤的函数。

咱们重点关注 wp_kses_hook() 这个函数,因为它内部使用了 _wp_kses_allowed_html 钩子。

function wp_kses_hook( $string, $allowed_html, $allowed_protocols ) {
    // ... (一些变量定义和初始化) ...

    /**
     * Filters the list of allowed tags and attributes when using wp_kses().
     *
     * @since 2.0.0
     *
     * @param array  $allowed_html     An array of allowed HTML tags and their allowed attributes.
     * @param string $string           The HTML string being filtered.
     * @param string $allowed_protocols An array of allowed protocols.
     */
    $allowed_html = apply_filters( '_wp_kses_allowed_html', $allowed_html, $string, $allowed_protocols );

    // ... (后续的 HTML 解析和过滤) ...

    return $string;
}

看到了吗?在 wp_kses_hook() 函数中,有一行非常关键的代码:

$allowed_html = apply_filters( '_wp_kses_allowed_html', $allowed_html, $string, $allowed_protocols );

这行代码使用了 apply_filters() 函数来调用 _wp_kses_allowed_html 钩子。也就是说,在进行 HTML 过滤之前,wp_kses_hook() 会先询问所有注册了 _wp_kses_allowed_html 钩子的函数,让它们有机会修改 $allowed_html 数组。

$allowed_html 数组的结构:HTML 白名单的“户口本”

$allowed_html 数组是 wp_kses() 用来存储 HTML 白名单的数据结构。它的结构如下:

$allowed_html = array(
    'tag_name' => array(
        'attribute_name' => true,
        'attribute_name' => true,
        // ...
    ),
    'another_tag_name' => array(
        'attribute_name' => true,
        // ...
    ),
    // ...
);

其中:

  • tag_name 是 HTML 标签的名称,比如 paimg 等。
  • attribute_name 是 HTML 属性的名称,比如 hrefsrcclass 等。
  • true 表示该属性是允许的。

举个例子,如果 $allowed_html 数组包含以下内容:

$allowed_html = array(
    'p' => array(
        'class' => true,
    ),
    'a' => array(
        'href' => true,
        'title' => true,
    ),
);

那么 wp_kses() 就会允许以下 HTML 代码:

<p class="my-class">This is a paragraph.</p>
<a href="https://example.com" title="Example">Example Link</a>

但是会拒绝以下 HTML 代码:

<p style="color: red;">This is a paragraph.</p>  <!-- style 属性被拒绝 -->
<a onclick="alert('Hello!');">Example Link</a>  <!-- onclick 属性被拒绝 -->

自定义白名单:给保安“升级”通行证

现在,咱们来看看如何使用 _wp_kses_allowed_html 钩子来自定义白名单。

假设我们想允许 img 标签的 data-src 属性,就可以在 functions.php 文件中添加以下代码:

function my_custom_kses_allowed_html( $allowed_html, $string, $allowed_protocols ) {
    $allowed_html['img']['data-src'] = true;
    return $allowed_html;
}
add_filter( '_wp_kses_allowed_html', 'my_custom_kses_allowed_html', 10, 3 );

这段代码做了以下几件事:

  1. 定义一个函数 my_custom_kses_allowed_html() 这个函数接收三个参数:
    • $allowed_html:当前的 HTML 白名单数组。
    • $string:要过滤的 HTML 字符串。
    • $allowed_protocols:允许的协议数组(比如 httphttps 等)。
  2. 修改 $allowed_html 数组: 在函数内部,我们修改 $allowed_html 数组,将 img 标签的 data-src 属性设置为 true,表示允许该属性。
  3. 返回修改后的 $allowed_html 数组: 函数必须返回修改后的 $allowed_html 数组,这样 wp_kses() 才能使用新的白名单进行过滤。
  4. 使用 add_filter() 函数注册钩子: add_filter( '_wp_kses_allowed_html', 'my_custom_kses_allowed_html', 10, 3 ); 这行代码将 my_custom_kses_allowed_html() 函数注册到 _wp_kses_allowed_html 钩子上。

    • '_wp_kses_allowed_html' 是钩子的名称。
    • 'my_custom_kses_allowed_html' 是要执行的函数名称。
    • 10 是优先级,数字越小优先级越高。
    • 3 是传递给函数的参数个数。

这样,wp_kses() 在过滤 HTML 代码时,就会允许 img 标签的 data-src 属性。

更复杂的例子:允许自定义标签和属性

假设我们需要允许自定义标签 <my-tag> 和自定义属性 my-attribute。可以这样做:

function my_custom_kses_allowed_html( $allowed_html, $string, $allowed_protocols ) {
    $allowed_html['my-tag'] = array(
        'my-attribute' => true,
    );
    return $allowed_html;
}
add_filter( '_wp_kses_allowed_html', 'my_custom_kses_allowed_html', 10, 3 );

这段代码会将 <my-tag my-attribute="value"> 加入到白名单中。

需要注意的点:安全第一!

虽然 _wp_kses_allowed_html 钩子非常强大,可以让你自定义 HTML 白名单,但是在使用时一定要小心谨慎,避免引入安全漏洞。

  • 只允许必要的标签和属性: 不要随意添加标签和属性,只允许那些确实需要的。
  • 对属性值进行验证: wp_kses() 只会检查属性是否存在于白名单中,不会验证属性值的安全性。因此,如果允许用户输入属性值,一定要进行额外的验证,防止 XSS 攻击。
  • 使用 wp_kses_post()wp_kses_data() WordPress 提供了 wp_kses_post()wp_kses_data() 函数,它们分别针对文章内容和普通数据进行了优化,使用了更安全的默认白名单。如果你的目的是过滤文章内容或普通数据,建议使用这两个函数。

wp_kses_post()wp_kses_data():更专业的保安

wp_kses_post()wp_kses_data() 函数都是对 wp_kses() 的封装,它们使用了不同的默认白名单,适用于不同的场景。

  • wp_kses_post() 用于过滤文章内容。它允许的标签和属性更多,更适合用于处理用户提交的富文本内容。
  • wp_kses_data() 用于过滤普通数据。它允许的标签和属性更少,更安全,适合用于处理用户提交的普通文本数据。

在使用 wp_kses_post()wp_kses_data() 时,也可以使用 _wp_kses_allowed_html 钩子来修改它们的默认白名单。

总结:wp_kses() + _wp_kses_allowed_html = 灵活安全的 HTML 过滤

wp_kses() 函数是 WordPress 中用于过滤 HTML 代码的重要工具。_wp_kses_allowed_html 钩子则为开发者提供了自定义白名单的强大能力。

通过使用 _wp_kses_allowed_html 钩子,你可以根据自己的需求,定制 wp_kses() 的过滤规则,既保证了网站的安全性,又满足了用户的个性化需求。

表格总结

函数/钩子 作用
wp_kses() 过滤 HTML 标签和属性,只允许白名单上的标签和属性通过。
_wp_kses_allowed_html 允许开发者修改 wp_kses() 默认的 HTML 白名单。
$allowed_html 存储 HTML 白名单的数据结构,是一个关联数组,键是标签名,值是允许的属性数组。
wp_kses_post() 用于过滤文章内容,使用了更适合富文本内容的默认白名单。
wp_kses_data() 用于过滤普通数据,使用了更安全的默认白名单。

代码示例总结

  1. 允许 img 标签的 data-src 属性:

    function my_custom_kses_allowed_html( $allowed_html, $string, $allowed_protocols ) {
        $allowed_html['img']['data-src'] = true;
        return $allowed_html;
    }
    add_filter( '_wp_kses_allowed_html', 'my_custom_kses_allowed_html', 10, 3 );
  2. 允许自定义标签 <my-tag> 和自定义属性 my-attribute

    function my_custom_kses_allowed_html( $allowed_html, $string, $allowed_protocols ) {
        $allowed_html['my-tag'] = array(
            'my-attribute' => true,
        );
        return $allowed_html;
    }
    add_filter( '_wp_kses_allowed_html', 'my_custom_kses_allowed_html', 10, 3 );

安全提示总结

  • 只允许必要的标签和属性。
  • 对属性值进行验证,防止 XSS 攻击。
  • 使用 wp_kses_post()wp_kses_data(),它们使用了更安全的默认白名单。

好了,今天的讲座就到这里。希望大家对 wp_kses()_wp_kses_allowed_html 钩子有了更深入的了解。记住,安全第一,合理使用,才能让你的 WordPress 网站更加安全可靠!感谢大家的观看,下次再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注