剖析 WordPress `wp_kses_allowed_html` 过滤器源码:如何自定义允许的 HTML 标签。

各位观众老爷们,晚上好!我是今晚的主讲人,江湖人称“代码老中医”,专治各种疑难杂症,今天咱们就来聊聊 WordPress 里一个神奇的过滤器:wp_kses_allowed_html,保证药到病除,让你彻底掌握自定义 WordPress 允许的 HTML 标签的秘诀。

一、 什么是 wp_kses?为什么要折腾 wp_kses_allowed_html

在深入 wp_kses_allowed_html 之前,咱们得先认识一下它的老大哥 wp_kseswp_kses 是 WordPress 用来过滤 HTML 内容的函数,防止用户输入恶意代码,保证网站安全。它就像一个尽职尽责的保安,不允许任何可疑的家伙进入你的 WordPress 网站。

但是!问题来了,保安太尽职了,有时候也会把一些“好人”也拦在门外。比如,你想在文章里插入一个 <video> 标签,或者一个自定义的 HTML5 标签,结果被 wp_kses 无情地咔嚓掉了。这时候,你就需要 wp_kses_allowed_html 出马了。

wp_kses_allowed_html 是一个过滤器,它允许你自定义 wp_kses 允许的 HTML 标签和属性。你可以通过这个过滤器,告诉 wp_kses:“嘿,这个 <video> 标签是好人,放它进来!”

二、 wp_kses_allowed_html 的工作原理:从源码说起

要理解 wp_kses_allowed_html 的工作原理,咱们就得扒开 WordPress 的裤衩,看看它的源码。

首先,wp_kses 函数的核心逻辑是,它会根据一个预定义的允许标签和属性列表,来判断哪些 HTML 标签和属性是允许的。这个列表就是通过 wp_kses_allowed_html 过滤器来修改的。

wp_kses_allowed_html 过滤器位于 wp-includes/kses.php 文件中(不同WordPress版本可能会有细微差异)。它在 wp_kses 函数内部被调用,大概是这么个意思:

function wp_kses( $data, $allowed_html, $allowed_protocols ) {
    // ... 一些代码 ...

    // 应用 wp_kses_allowed_html 过滤器
    $allowed_html = apply_filters( 'wp_kses_allowed_html', $allowed_html, $data, $allowed_protocols );

    // ... 剩下的过滤逻辑 ...
}

看到没?apply_filters( 'wp_kses_allowed_html', $allowed_html, $data, $allowed_protocols ); 这行代码就是关键。它会调用所有注册到 wp_kses_allowed_html 过滤器的函数,并将当前允许的 HTML 标签列表 $allowed_html 作为参数传递给这些函数。

这意味着,你可以在你的主题或者插件里,注册一个函数到 wp_kses_allowed_html 过滤器,修改 $allowed_html 数组,从而达到自定义允许标签的目的。

三、 如何使用 wp_kses_allowed_html:实战演练

好了,理论讲完了,咱们来点实际的。假设我们想允许 <video> 标签,并允许它拥有 src, width, height, controls 属性。我们可以这样做:

  1. 在你的主题的 functions.php 文件或者插件里,添加以下代码:
<?php
add_filter( 'wp_kses_allowed_html', 'allow_video_tag', 10, 2 );

function allow_video_tag( $allowed_tags, $context ) {

    if ( current_user_can('edit_posts') || current_user_can('edit_pages') ) {
        $allowed_tags['video'] = array(
            'src'      => true,
            'width'    => true,
            'height'   => true,
            'controls' => true,
            'poster'   => true, // 允许 poster 属性
            'preload'  => true,  // 允许 preload 属性
            'loop'     => true,   // 允许 loop 属性
            'muted'    => true,   // 允许 muted 属性
            'autoplay' => true    // 允许 autoplay 属性
        );

        $allowed_tags['source'] = array(
            'src'  => true,
            'type' => true
        );
    }

    return $allowed_tags;
}
?>
  1. 代码解释:

    • add_filter( 'wp_kses_allowed_html', 'allow_video_tag', 10, 2 );:这行代码将 allow_video_tag 函数注册到 wp_kses_allowed_html 过滤器。 10 是优先级,数字越小优先级越高。 2 表示传递给 allow_video_tag 函数的参数个数,分别是 $allowed_tags$context
    • function allow_video_tag( $allowed_tags, $context ) { ... }:这是我们的自定义函数,它接收两个参数:
      • $allowed_tags:当前允许的 HTML 标签列表,是一个数组。
      • $context:当前 wp_kses 函数的上下文,例如 post, comment 等。我们可以根据上下文来决定是否允许某些标签。
    • if ( current_user_can('edit_posts') || current_user_can('edit_pages') ) { ... }:这行代码判断当前用户是否有编辑文章或页面的权限。只有有权限的用户才能使用 <video> 标签。这是一种安全措施,防止恶意用户滥用。
    • $allowed_tags['video'] = array( ... );:这行代码将 <video> 标签添加到允许列表中,并指定了允许的属性。 true 表示允许该属性。
    • return $allowed_tags;:最后,我们必须返回修改后的 $allowed_tags 数组,否则 wp_kses 还是会使用默认的允许列表。
  2. 使用 <video> 标签:

    现在,你就可以在你的文章或者页面里使用 <video> 标签了:

<video src="your-video.mp4" width="640" height="360" controls poster="your-poster.jpg">
  Your browser does not support the video tag.
</video>

四、 深入理解 $allowed_tags 数组的结构:

$allowed_tags 数组是一个多维数组,它的结构如下:

$allowed_tags = array(
    'tag_name' => array(
        'attribute_name' => true,
        'another_attribute' => true,
        // ...
    ),
    'another_tag' => array(
        // ...
    ),
    // ...
);
  • tag_name:HTML 标签的名字,例如 'video', 'p', 'a' 等。
  • attribute_name:HTML 属性的名字,例如 'src', 'href', 'class' 等。
  • true:表示允许该属性。

更复杂的属性控制:协议过滤

有时候,我们不仅要允许某个属性,还要限制它的值。比如,我们想允许 <a> 标签的 href 属性,但只允许 http, https, mailto 协议。这时候,我们可以使用协议过滤。

$allowed_tags['a'] = array(
    'href' => array( 'http', 'https', 'mailto' ),
    'title' => true,
    'class' => true,
    'target'=> true // 允许 target 属性
);

这里,'href' => array( 'http', 'https', 'mailto' ) 表示只允许 href 属性的值以 http, https, mailto 开头。

五、 常用 HTML 标签及其属性的配置示例:

为了方便大家,我整理了一些常用 HTML 标签及其属性的配置示例:

标签 常用属性 配置示例
a href, title, target, class, rel php 'a' => array( 'href' => array( 'http', 'https', 'mailto' ), 'title' => true, 'target' => true, 'class' => true, 'rel' => true )
img src, alt, width, height, class, srcset, sizes php 'img' => array( 'src' => true, 'alt' => true, 'width' => true, 'height' => true, 'class' => true, 'srcset' => true, 'sizes' => true )
p class, style php 'p' => array( 'class' => true, 'style' => true )
div class, style, id, align php 'div' => array( 'class' => true, 'style' => true, 'id' => true, 'align' => true )
span class, style php 'span' => array( 'class' => true, 'style' => true )
h1h6 class, style, id php 'h1' => array( 'class' => true, 'style' => true, 'id' => true ), 'h2' => array( 'class' => true, 'style' => true, 'id' => true ), // ...
ul, ol class, style php 'ul' => array( 'class' => true, 'style' => true ), 'ol' => array( 'class' => true, 'style' => true )
li class, style php 'li' => array( 'class' => true, 'style' => true )
table class, style, width, border, cellpadding, cellspacing php 'table' => array( 'class' => true, 'style' => true, 'width' => true, 'border' => true, 'cellpadding' => true, 'cellspacing' => true )
tr, td, th class, style, align, valign, colspan, rowspan php 'tr' => array( 'class' => true, 'style' => true, 'align' => true, 'valign' => true ), 'td' => array( 'class' => true, 'style' => true, 'align' => true, 'valign' => true, 'colspan' => true, 'rowspan' => true ), 'th' => array( 'class' => true, 'style' => true, 'align' => true, 'valign' => true, 'colspan' => true, 'rowspan' => true )
blockquote class, style, cite php 'blockquote' => array( 'class' => true, 'style' => true, 'cite' => true )
code class php 'code' => array( 'class' => true )
pre class php 'pre' => array( 'class' => true )
video src, width, height, controls, poster, preload, loop, muted, autoplay php 'video' => array( 'src' => true, 'width' => true, 'height' => true, 'controls' => true, 'poster' => true, 'preload' => true, 'loop' => true, 'muted' => true, 'autoplay' => true )
source src, type php 'source' => array( 'src' => true, 'type' => true )
audio src, controls, preload, loop, muted, autoplay php 'audio' => array( 'src' => true, 'controls' => true, 'preload' => true, 'loop' => true, 'muted' => true, 'autoplay' => true )
iframe src, width, height, frameborder, allowfullscreen (谨慎使用,需要严格验证 src 属性,防止 XSS 攻击!) php 'iframe' => array( 'src' => true, 'width' => true, 'height' => true, 'frameborder' => true, 'allowfullscreen' => true ) 重要提示: iframe 标签非常危险,因为它允许嵌入外部网页。如果 src 属性没有经过严格验证,可能会导致 XSS 攻击。请谨慎使用,并确保只允许可信任的来源。

六、 安全注意事项:防范 XSS 攻击

wp_kses_allowed_html 过滤器虽然强大,但同时也带来了安全风险。如果你不小心允许了恶意标签或者属性,可能会导致 XSS (Cross-Site Scripting) 攻击。

XSS 攻击的原理:

XSS 攻击是指攻击者通过在你的网站上注入恶意脚本,当其他用户访问你的网站时,这些脚本会在用户的浏览器上执行,从而窃取用户的信息或者进行其他恶意操作。

如何防范 XSS 攻击:

  • 最小权限原则: 只允许必要的标签和属性。不要为了方便而允许一些不常用的标签。
  • 严格验证属性值: 对于一些敏感的属性,例如 href, src, style,要进行严格的验证,确保它们的值是安全的。可以使用 WordPress 提供的函数,例如 esc_url(), esc_attr(), wp_kses_post() 等,对属性值进行转义。
  • 使用白名单: 尽量使用白名单,只允许特定的标签和属性,而不是使用黑名单,禁止一些不安全的标签和属性。黑名单很容易被绕过,而白名单则更加安全。
  • 定期更新 WordPress 和插件: WordPress 和插件的开发者会不断修复安全漏洞,所以要定期更新它们,确保你的网站使用的是最新的安全版本。
  • 使用安全插件: 可以使用一些安全插件,例如 Wordfence, Sucuri Security 等,来增强你的网站的安全性。

七、 调试技巧:让错误无处遁形

在使用 wp_kses_allowed_html 过滤器时,可能会遇到一些问题,例如:

  • 标签没有生效:可能是你的代码有错误,或者优先级太低,被其他过滤器覆盖了。
  • 属性没有生效:可能是你没有正确配置 $allowed_tags 数组,或者属性名写错了。

如何调试:

  • 使用 var_dump() 或者 print_r() 函数: 在你的 allow_video_tag 函数里,使用 var_dump( $allowed_tags ); 或者 print_r( $allowed_tags ); 函数,可以查看 $allowed_tags 数组的内容,看看你的修改是否生效。
  • 使用 error_log() 函数: 将错误信息写入到 WordPress 的错误日志里。例如,error_log( 'My custom error message' );
  • 使用浏览器开发者工具: 查看网页的源代码,看看 HTML 标签是否被正确渲染。
  • 禁用其他插件: 禁用其他插件,看看是否是插件冲突导致的。
  • 检查主题的 functions.php 文件: 确保你的代码没有语法错误,并且被正确加载。

八、 高级用法:根据用户角色允许不同的标签

有时候,我们希望根据用户的角色,允许不同的 HTML 标签。例如,我们只允许管理员使用 <script> 标签。可以这样做:

<?php
add_filter( 'wp_kses_allowed_html', 'allow_script_tag_for_admins', 10, 2 );

function allow_script_tag_for_admins( $allowed_tags, $context ) {

    if ( current_user_can('administrator') ) {
        $allowed_tags['script'] = array(
            'type' => true,
            'src'  => true
        );
    }

    return $allowed_tags;
}
?>

这段代码只允许管理员使用 <script> 标签。其他用户会被 wp_kses 过滤掉。

九、 总结:wp_kses_allowed_html 的威力与责任

wp_kses_allowed_html 过滤器是一个强大的工具,它可以让你自定义 WordPress 允许的 HTML 标签和属性,从而实现更灵活的内容编辑。但是,它也带来了安全风险。在使用 wp_kses_allowed_html 过滤器时,一定要牢记安全第一,防范 XSS 攻击。

记住,能力越大,责任越大! 掌握 wp_kses_allowed_html 的秘诀后,别忘了用它来保护你的 WordPress 网站,而不是破坏它。

好了,今天的讲座就到这里。希望大家都能成为 wp_kses_allowed_html 的高手,打造更安全、更强大的 WordPress 网站! 散会!

发表回复

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