操作环境
操作系统信息:
master@ubuntu:~$ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 18.04.4 LTS Release: 18.04 Codename: bionic
WordPress 信息:
主程序版本:WordPress 5.3.2
主题及版本:Twenty Sixteen(版本:2.0, 由WordPress团队开发)
需求分析
运行在公网中的 WordPress 站点极易遭受 SQL 注入攻击以及 XSS 跨站脚本攻击等。即便是一个不开放用户注册功能的个人博客,也存在攻击切入点,例如评论框。目前版本的 WordPress (WordPress 5.3.2) 本身并不会把可能包含恶意代码的评论删除,在 WordPress 没有被暴露出漏洞的时候,这么做可能并没有什么问题,但是一旦被发现存在漏洞,那么允许包含恶意代码的数据存入数据库就会变得很危险。因此,我的需求就是,一旦评论中被检测到存在可能的恶意代码,就要把该评论删除,同时,用一条表达警示信息的预置标准评论代替该评论,从而使得任何可能包含恶意代码的评论都不被写入数据库,最大程度地防范 SQL 注入攻击和 XSS 跨站脚本攻击等。
格式说明
- 在本文中所出现的代码中,凡是我自己定义的函数和变量等,一般都会以
zkf_作为其前缀; - 没有使用
zkf_作为前缀的函数或者变量等,一般情况下都是 WordPress 平台或者 PHP 提供的。 - 在没有特殊说明的情况下,下文中出现的 “WordPress” 和 “WP” 所代表的是相同的含义。
- 本文代码块中出现的
... ...表示这些位置还存在其他代码,但与当前要说明的问题关系不大,故省略之。
实现过程
前面的需求可以转换成如下步骤,如图 1:
要获取评论内容,我们就需要使用 WordPress 平台提供的如下函数:
wp_insert_comment( array $commentdata )
关于该函数的更多详细信息参见 References [1].
该函数的作用是将一个评论插入数据库中。该函数提供了很多参数,我们只需要使用其中一个参数,如下:
comment_content
comment_content 参数可以返回评论的内容,类型为 string.
在 wp-includes/comment.php 文件中,我们可以找到如下钩子激活点:
function wp_insert_comment( $commentdata ) {
... ...
/**
* Fires immediately after a comment is inserted into the database.
*
* @since 2.8.0
*
* @param int $id The comment ID.
* @param WP_Comment $comment Comment object.
*/
do_action( 'wp_insert_comment', $id, $comment );
... ...
}
去掉注释更清楚:
function wp_insert_comment( $commentdata ) {
... ...
do_action( 'wp_insert_comment', $id, $comment );
... ...
}
关于该钩子激活点的更多详细信息参见 References [2].
由上面提到的 do_action( 'wp_insert_comment', $id, $comment ); 钩子激活点的格式我们知道,wp_insert_comment 这个钩子函数可以提供两个参数,分别是评论 ID $id 和评论内容 $comment. 因此,在定义动作函数 zkf_comment_check() 时,我们同样需要为其设置两个参数,这样才能完成挂载。
接下来,使用 add_action 将 zkf_comment_check() 函数挂载到 wp_insert_comment, 我们之后的实现代码将写在 zkf_comment_check() 动作函数中:
add_action('wp_insert_comment', 'zkf_comment_check', 10, 2);
根据上面的代码,我们知道,该挂载点的优先级为 10, 需要为该挂载点 (即 zkf_comment_check() 函数) 传入参数的个数为 2.
接下来,开始实现 zkf_comment_check() 函数。
首先定义 zkf_comment_check() 函数并为其传入两个参数:
function zkf_comment_check($zkf_comment_id, $zkf_comment_object){
}
之后,开始在 zkf_comment_check() 函数中写入具体的实现代码。
首先创建一个数组,命名为 $zkf_commentarr:
$zkf_commentarr = array();
接着使用从钩子函数那里接收到的评论 ID 为数组赋值:
$zkf_commentarr['comment_ID'] = $zkf_comment_id;
在上面的代码中,我们用到了 WP 平台提供的 comment_ID 函数,该函数的作用是返回当前评论的 ID. 详情可参见 References [3].
之后,通过 comment_content 参数获取当前评论的内容:
$zkf_new_comment = $zkf_comment_object->comment_content;
之后,将评论中的字母全部转换成小写,作为防止大小写绕过的第一道关卡:
$zkf_new_comment_check = strtolower($zkf_new_comment);
通过分析常见的 SQL 注入语句和 XSS 跨站脚本攻击代码,我总结出了如下危险关键字,一旦评论中出现了这些关键字,则评论者极有可能是在尝试进行攻击:
- +
- #
- —
- %
- ||
- @
- @@
- insert
- update
- delete
- and
- or
- union
- select
- from
- where
- limit
- order by
- guoup by
我们要对上面这些危险关键字进行过滤,实现代码如下:
$zkf_str = "+\\#\\--\\%\\||\\@\\@@\\insert\\update\\delete\\and\\or\\union\\select\\from\\where\\limit\\order by\\guoup by\\<script>\\</script>";
使用 \\ 做分割标记,将字符打散为数组 $zkf_arr:
$zkf_arr=explode("\\",$zkf_str);
接下来,使用 PHP 提供的 foreach() 函数,对数组 $zkf_arr 中的每一个键值进行遍历,之后,在 foreach() 函数中使用 stripos 函数对评论内容和取出的键值进行忽略大小写的比较(这里是防止大小写绕过的第二道关卡)并将比对的结果赋值给 $zkf_flag. 若 $zkf_flag = flase, 则代表评论中不包含预置的危险代码,若 $zkf_flag = true, 则代表评论中包含预置的危险代码,此时,评论内容会被替换成“《该评论中发现危险代码,已被箭幕防御系统危险评论拦截模块删除。》”,原来含有危险代码的评论会被丢弃,不会写入数据库。代码如下:
foreach($zkf_arr as $zkf_key => $zkf_val){
//使用 PHP strpos() 函数对内容进行过滤
//stripos() 函数不区分大小写
//strpos() 函数区分大小写
/*
前面已经将所有字符都转换成小写了
这里使用 stripos() 函数再次忽略大小写
可以起到双保险的作用
*/
$zkf_flag=stripos($zkf_new_comment_check,$zkf_val);
if ($zkf_flag){
$zkf_commentarr['comment_content'] = "《该评论中发现危险代码,已被箭幕防御系统危险评论拦截模块删除。》";
}
}
去掉注释更清楚:
foreach($zkf_arr as $zkf_key => $zkf_val){
$zkf_flag=stripos($zkf_new_comment_check,$zkf_val);
if ($zkf_flag){
$zkf_commentarr['comment_content'] = "《该评论中发现危险代码,已被箭幕防御系统危险评论拦截模块删除。》";
}
}
最后,使用 wp_update_comment 函数对数据库中已经存在的评论进行一次更新,关于该函数的更多详细信息参见 References [4].
完整的实现代码如下:
//危险评论内容过滤模块
//添加评论时触发 zkf_comment_check() 函数
add_action('wp_insert_comment', 'zkf_comment_check', 10, 2);
//定义 zkf_comment_check() 函数
//传入评论 ID 和评论内容两个参数
function zkf_comment_check($zkf_comment_id, $zkf_comment_object){
//创建数组 $zkf_commentarr
$zkf_commentarr = array();
//PHP 会把单引号里的内容当成纯文本
//单引号里面是什么就理解成什么
//PHP 中的方括号[]和花括号{}都可以用来访问数组单元对应的值
//为数组赋值
$zkf_commentarr['comment_ID'] = $zkf_comment_id;
//获取当前的评论内容
$zkf_new_comment = $zkf_comment_object->comment_content;
//使用 PHP strtolower() 函数将评论内容中的
//将所有字母都转换成小写
$zkf_new_comment_check = strtolower($zkf_new_comment);
//把数据库查询代码和JS标记代码都视作危险代码
$zkf_str = "+\\#\\--\\%\\||\\@\\@@\\insert\\update\\delete\\and\\or\\union\\select\\from\\where\\limit\\order by\\guoup by\\<script>\\</script>";
//使用 \\ 做分割标记,将字符打散为数组
$zkf_arr=explode("\\",$zkf_str);
foreach($zkf_arr as $zkf_key => $zkf_val){
//使用 PHP strpos() 函数对内容进行过滤
//stripos() 函数不区分大小写
//strpos() 函数区分大小写
/*
前面已经将所有字符都转换成小写了
这里使用 stripos() 函数再次忽略大小写
可以起到双保险的作用
*/
$zkf_flag=stripos($zkf_new_comment_check,$zkf_val);
if ($zkf_flag){
$zkf_commentarr['comment_content'] = "《该评论中发现危险代码,已被箭幕防御系统危险评论拦截模块删除。》";
}
}
//对数据库中已经存在的评论进行更新
wp_update_comment($zkf_commentarr);
}
去掉注释更清楚:
add_action('wp_insert_comment', 'zkf_comment_check', 10, 2);
function zkf_comment_check($zkf_comment_id, $zkf_comment_object){
$zkf_commentarr = array();
$zkf_commentarr['comment_ID'] = $zkf_comment_id;
$zkf_new_comment = $zkf_comment_object->comment_content;
$zkf_new_comment_check = strtolower($zkf_new_comment);
$zkf_str = "+\\#\\--\\%\\||\\@\\@@\\insert\\update\\delete\\and\\or\\union\\select\\from\\where\\limit\\order by\\guoup by\\<script>\\</script>";
$zkf_arr=explode("\\",$zkf_str);
foreach($zkf_arr as $zkf_key => $zkf_val){
$zkf_flag=stripos($zkf_new_comment_check,$zkf_val);
if ($zkf_flag){
$zkf_commentarr['comment_content'] = "《该评论中发现危险代码,已被箭幕防御系统危险评论拦截模块删除。》";
}
}
wp_update_comment($zkf_commentarr);
}
运行效果如图 2 所示:

References:
[1]. wp_insert_comment() | Function | WordPress Developer Resources
https://developer.wordpress.org/reference/functions/wp_insert_comment/
[2]. wp_insert_comment | Hook | WordPress Developer Resources
https://developer.wordpress.org/reference/hooks/wp_insert_comment/
[3]. comment_ID() | Function | WordPress Developer Resources
https://developer.wordpress.org/reference/functions/comment_id/
[4]. wp_update_comment() | Function | WordPress Developer Resources
https://developer.wordpress.org/reference/functions/wp_update_comment/
EOF