Xiuno BBS 审计之问题:钩子优先级冲突无仲裁
贰先生 5小时前

此文章为XIUNOX版本重构审计时发现问题,XIUNOX版本已优化修复此问题。分享出来方便后续想基于xiuno bbs4.0.4版本制作维护版本或插件模板等需求的开发者和站长参考。

现象

Xiuno BBS 4.0.4 通过 <!--{hook xxx}--> / // hook xxx 标记在源文件中插入钩子点,多插件可同时挂载同一钩子。系统提供 hooks_rank 字段用于排序,但存在严重设计缺陷:

  1. 默认 rank 为 0,多个插件未声明 rank 时全部并列 0,最终顺序由 glob() 返回顺序决定(受文件系统影响,不确定)。
  2. 当两个插件声明相同 rank(如都设 10)时,无任何仲裁机制,执行顺序不可预期。
  3. 钩子内容直接被字符串拼接进编译后文件,多插件同时修改同一变量(如 $user$thread)会互相覆盖,无隔离。
  4. rank 仅影响顺序,无法表达"独占钩子""互斥挂载"等约束。

源码证据

文件:xiunobbs_4.0.4/model/plugin.func.php

// 342-389 行:钩子编译回调
function plugin_compile_srcfile_callback($m) {
    static $hooks;
    if(empty($hooks)) {
        $hooks = array();
        $plugin_paths = plugin_paths_enabled();

        foreach($plugin_paths as $path=>$pconf) {
            $dir = file_name($path);
            $hookpaths = glob(APP_PATH."plugin/$dir/hook/*.*"); // path
            if(is_array($hookpaths)) {
                foreach($hookpaths as $hookpath) {
                    $hookname = file_name($hookpath);
                    $rank = isset($pconf['hooks_rank']["$hookname"]) ? $pconf['hooks_rank']["$hookname"] : 0;
                    $hooks[$hookname][] = array('hookpath'=>$hookpath, 'rank'=>$rank);
                }
            }
        }
        // 仅按 rank 倒序排序,rank 相同时顺序由 array 多键排序内部稳定性决定(不保证)
        foreach ($hooks as $hookname=>$arrlist) {
            $arrlist = arrlist_multisort($arrlist, 'rank', FALSE);
            $hooks[$hookname] = arrlist_values($arrlist, 'hookpath');
        }
    }

    $s = '';
    $hookname = $m[1];
    if(!empty($hooks[$hookname])) {
        $fileext = file_ext($hookname);
        foreach($hooks[$hookname] as $path) {
            $t = file_get_contents($path);
            if($fileext == 'php' && preg_match('#^\s*<\?php\s+exit;#is', $t)) {
                $t = preg_replace('#^\s*<\?php\s*exit;(.*?)(?:\?>)?\s*$#is', '\\1', $t);
            }
            $s .= $t;   // 直接字符串拼接,无隔离、无仲裁
        }
    }
    return $s;
}

文件:xiunobbs_4.0.4/model/plugin.func.php

// 462 行:hooks_rank 默认为空数组,绝大多数插件不会声明
!isset($local['hooks_rank']) && $local['hooks_rank'] = array();

风险等级与结论

风险等级:高(High)|生态缺陷

危害:

  1. 多插件同钩子无仲裁,安装顺序、文件系统顺序决定执行顺序,行为不可重现。
  2. 钩子内容直接拼接为字符串,两个插件若同时修改 $thread['subject']  $user 等共享变量,会引发静默数据覆盖,难以排查。
  3. 没有钩子优先级冲突检测,安装新插件时不会警告"该钩子已被另一插件占用且 rank 相同"。
  4. 插件作者无法声明"我必须最后执行""我必须最先执行""此钩子互斥"等语义,生态协作困难。

修复建议:

  • 默认 rank 改为按插件安装顺序自动递增(如 installed_index * 100),未声明时仍有可预期顺序。
  • 同 rank 冲突时,引入二级排序键(插件 dir 字典序),保证可重现。
  •  plugin_compile_srcfile_callback 中检测同 hook 多挂载情况,记录到日志或运行时警告。
  • 提供"钩子独占"声明字段 hooks_exclusive,安装时若发现冲突直接拒绝。
  • 引入钩子命名空间隔离,例如自动包裹 function plugin_xxx_hookname() { ... },避免变量污染。
最新回复 (0)
全部楼主
返回