各位观众老爷们,晚上好! 今天咱们不开车,来聊聊WordPress安全里一个重要的角色——wp_kses()
。 这家伙的名字听起来像个秘密特工,实际上,它就是WordPress的HTML净化大师,专门负责把那些可能搞破坏的HTML标签和属性给咔嚓掉,防止XSS攻击。
XSS:Web安全界的“小强”
XSS (Cross-Site Scripting,跨站脚本攻击) 绝对是Web安全领域里生命力最顽强的“小强”之一。 简单来说,就是攻击者往你的网站里塞一些恶意的JavaScript代码,当用户浏览你的网站时,这些代码就会在用户的浏览器里执行,从而窃取用户的信息、篡改页面内容,甚至冒充用户身份搞事情。
想象一下,你辛辛苦苦搭建的博客,结果被别人插了广告,或者更过分,直接跳转到钓鱼网站,是不是想提刀砍人? 所以,防范XSS攻击至关重要。
wp_kses()
:白名单才是王道
wp_kses()
函数的核心思想是“白名单”,而不是“黑名单”。 啥意思呢? 就是说,它不会去尝试识别所有可能的恶意标签和属性(这几乎是不可能的,因为攻击手段层出不穷),而是维护一个允许使用的标签和属性的列表,只有在这个列表里的HTML才能幸存下来,其他的统统干掉。
这种方式的好处是,即使出现新的攻击手段,只要不在白名单里,就无法生效。 相比之下,黑名单的方式很容易被绕过,因为总有你没想到的攻击方式。
wp_kses()
的基本用法
wp_kses()
函数的基本用法如下:
$allowed_html = array(
'a' => array(
'href' => array(),
'title' => array(),
),
'br' => array(),
'em' => array(),
'strong' => array(),
);
$string = '<a href="https://example.com" title="Example">Example Link</a><br><em>Emphasis</em> <strong>Strong</strong> <script>alert("XSS");</script>';
$ksesed_string = wp_kses( $string, $allowed_html );
echo $ksesed_string; // 输出:<a href="https://example.com" title="Example">Example Link</a><br><em>Emphasis</em> <strong>Strong</strong>
在这个例子中,我们定义了一个 $allowed_html
数组,它指定了允许使用的HTML标签和属性。 然后,我们把一个包含恶意脚本的字符串传递给 wp_kses()
函数,经过处理后,恶意脚本被移除了,只留下了白名单里的标签和属性。
深入 wp_kses()
的源码
要真正理解 wp_kses()
的强大之处,我们需要深入它的源码。 别怕,我会尽量用通俗易懂的方式来讲解。
wp_kses()
函数位于 wp-includes/kses.php
文件中。 它的主要流程如下:
- 预处理: 对输入字符串进行一些预处理,例如移除BOM头。
- HTML解析: 使用正则表达式将HTML字符串分解成标签、属性和文本节点。
- 标签过滤: 遍历所有标签,检查它们是否在白名单中。 如果不在,则移除该标签。
- 属性过滤: 对于白名单中的标签,遍历它们的属性,检查属性是否在白名单中。 如果不在,则移除该属性。 还会进行属性值的安全检查,例如检查URL是否合法。
- 后处理: 将过滤后的标签、属性和文本节点重新组合成HTML字符串。
核心函数:wp_kses_split()
wp_kses_split()
函数是HTML解析的关键,它使用正则表达式将HTML字符串分解成不同的部分。 它的核心代码如下:
function wp_kses_split( $string, $allowed_html, $allowed_protocols ) {
$string = preg_replace( '/<!--.*?-->/s', '', $string ); // Remove comments
$string = preg_replace( '/<!--(.*)-->/Uis', '', $string ); // Remove comments
$string = preg_replace('/[rnt ]+/', ' ', $string);
preg_match_all('%
(
<[^<>]+
|
[^<>]+
)
%xs', $string, $matches );
$chunks = array();
foreach ( $matches[1] as $i => $match ) {
if ( '<' === $match[0] ) {
if ( 0 === strpos( $match, '<!--' ) ) {
continue;
}
$pieces = wp_kses_attr( $match, $allowed_html, $allowed_protocols );
if ( empty( $pieces ) ) {
continue;
}
$chunks[] = $pieces;
} else {
$chunks[] = wp_kses_no_null( wp_kses_html_error( $match ) );
}
}
return implode( '', $chunks );
}
这个函数使用正则表达式 /</?w.*?>/
来匹配HTML标签。 然后,它会调用 wp_kses_attr()
函数来处理标签的属性。
核心函数:wp_kses_attr()
wp_kses_attr()
函数负责过滤标签的属性。 它的核心代码如下:
function wp_kses_attr( $element, $allowed_html, $allowed_protocols ) {
$htmlregex = ''; // Build regex based on $allowed_html
$allowed_atts = array();
foreach ( (array) $allowed_html as $tag => $atts ) {
if ( ! isset( $atts['*'] ) ) {
continue;
}
$htmlregex .= '<' . preg_quote( $tag ) . '[^>]*>|';
foreach ( (array) $atts as $att => $val ) {
$allowed_atts[ $tag ][ $att ] = true;
}
}
$htmlregex = substr( $htmlregex, 0, -1 ); // Cut off the last '|'
$htmlregex = '/(' . $htmlregex . ')/i';
preg_match_all( '/(<[^<>]+>)/i', $element, $matches );
if ( empty( $matches[1] ) ) {
return '';
}
$element = $matches[1][0];
preg_match_all( '%(?s) ([a-zA-Z_:]+)=(['"])([^2]*?)2%', $element, $atts );
preg_match_all( '%(?s) ([a-zA-Z_:]+)=([^ '"]*)%', $element, $u_atts );
$attributes = array();
if ( ! empty( $atts[1] ) ) {
foreach ( (array) $atts[1] as $i => $name ) {
$attributes[ strtolower( $name ) ] = $atts[3][ $i ];
}
}
if ( ! empty( $u_atts[1] ) ) {
foreach ( (array) $u_atts[1] as $i => $name ) {
$attributes[ strtolower( $name ) ] = $u_atts[2][ $i ];
}
}
if ( empty( $attributes ) ) {
return $element;
}
$output = '<' . substr( $element, 1, strpos( $element, ' ' ) - 1 );
foreach ( (array) $attributes as $name => $value ) {
if ( ! isset( $allowed_atts[ strtolower( substr( $element, 1, strpos( $element, ' ' ) - 1 ) ) ][ $name ] ) ) {
continue;
}
$value = wp_kses_bad_protocol( $value, $allowed_protocols );
$value = esc_attr( $value );
$output .= ' ' . $name . '="' . $value . '"';
}
$output .= '>';
return $output;
}
这个函数首先提取出标签的所有属性,然后遍历这些属性,检查它们是否在白名单中。 如果属性在白名单中,它还会调用 wp_kses_bad_protocol()
函数来检查属性值是否包含恶意协议,例如 javascript:
。
核心函数:wp_kses_bad_protocol()
wp_kses_bad_protocol()
函数负责检查属性值是否包含恶意协议。 它的核心代码如下:
function wp_kses_bad_protocol( $string, $allowed_protocols ) {
$string = wp_kses_no_null( $string );
$string = strtolower( $string );
// Unencoded entities.
$string = wp_kses_decode_entities( $string );
$string = preg_replace( '/s/', '', $string );
do {
$original_string = $string;
foreach ( (array) $allowed_protocols as $protocol ) {
$string = str_replace( $protocol . ':', '', $string );
}
} while ( $original_string !== $string );
return $string;
}
这个函数首先移除字符串中的空格和NULL字符,然后将字符串转换为小写。 接着,它遍历所有允许的协议,例如 http
和 https
,将字符串中包含这些协议的部分移除。 如果移除后字符串仍然包含其他协议,则说明存在恶意协议。
自定义白名单:让 wp_kses()
更灵活
虽然 wp_kses()
已经提供了一个默认的白名单,但在实际应用中,我们可能需要自定义白名单,以满足特定的需求。 例如,我们可能需要允许使用 <iframe>
标签来嵌入视频,或者允许使用 data-
属性来存储自定义数据。
要自定义白名单,我们可以使用 wp_kses_allowed_html
过滤器。 例如,以下代码允许使用 <iframe>
标签,并允许使用 src
和 width
属性:
function my_kses_allow_iframe( $allowed_tags, $context ) {
if ( $context === 'post' ) { // 只在文章内容中使用
$allowed_tags['iframe'] = array(
'src' => true,
'width' => true,
'height' => true,
'frameborder' => true,
'allowfullscreen' => true,
);
}
return $allowed_tags;
}
add_filter( 'wp_kses_allowed_html', 'my_kses_allow_iframe', 10, 2 );
常见问题与注意事项
- 过度过滤: 过度过滤可能会导致一些合法的HTML标签被移除,从而影响页面的显示效果。 因此,我们需要仔细权衡安全性和可用性,选择合适的白名单。
- 编码问题: 在处理HTML字符串时,需要注意编码问题,例如确保字符串是UTF-8编码。
- 上下文: 不同的上下文可能需要不同的白名单。 例如,文章内容和评论内容可能需要不同的过滤规则。
- 更新: 随着Web安全技术的不断发展,攻击手段也在不断变化。 因此,我们需要定期更新WordPress,以获取最新的安全补丁和过滤规则。
- 不要完全依赖
wp_kses()
:wp_kses()
是一个强大的工具,但它不是万能的。 在处理用户输入时,我们还需要采取其他安全措施,例如输入验证和输出编码。
wp_kses()
白名单示例
标签 | 属性 | 说明 |
---|---|---|
a |
href , title , target (可能需要限制 target 属性的值,例如只允许 _blank ) |
链接标签 |
abbr |
title |
缩写标签 |
acronym |
title |
缩写标签 (已废弃,建议使用 <abbr> ) |
b |
(无属性) | 粗体标签 |
blockquote |
cite |
引用标签 |
br |
(无属性) | 换行标签 |
code |
(无属性) | 代码标签 |
em |
(无属性) | 强调标签 |
i |
(无属性) | 斜体标签 |
img |
src , alt , width , height , class , srcset , sizes (需要对 src 属性进行严格的URL验证) |
图片标签 |
li |
class |
列表项标签 |
ol |
class , start |
有序列表标签 |
p |
class |
段落标签 |
q |
cite |
短引用标签 |
small |
(无属性) | 小号字体标签 |
span |
class , title |
行内容器标签 |
strong |
(无属性) | 强调标签 |
sub |
(无属性) | 下标标签 |
sup |
(无属性) | 上标标签 |
ul |
class |
无序列表标签 |
总结
wp_kses()
函数是WordPress安全体系中一个非常重要的组成部分。 通过使用白名单机制,它可以有效地防止XSS攻击,保护你的网站和用户的安全。 但是,我们需要理解 wp_kses()
的工作原理,并根据实际需求自定义白名单,才能充分发挥它的作用。 同时,我们也要意识到 wp_kses()
不是万能的,还需要结合其他安全措施,才能构建一个安全可靠的WordPress网站。
好了,今天的讲座就到这里。 感谢各位的观看,希望对大家有所帮助! 记住,安全无小事,防范XSS,人人有责!