Xiuno BBS 审计之问题:无依赖注入容器,强耦合
贰先生 7小时前

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

现象

Xiuno BBS 4.0.4 没有依赖注入容器(DI Container),核心依赖对象($db$cache$conf$lang)通过两种方式暴露给业务代码:一是 global $var 声明(与"全局变量泛滥"缺陷叠加),二是塞入 $_SERVER 超全局变量(如 $_SERVER['db']$_SERVER['cache'])。函数内部用 _SERVER('db')  global $db 取依赖,无法 mock、无法替换、无法测试、无法支持多实例。$_SERVER 超全局变量本应仅用于存储 PHP 服务器环境信息,被滥用为容器。

源码证据

1. xiunophp.php 把核心依赖塞入 $_SERVER 充当容器

文件:xiunobbs_4.0.4/xiunophp/xiunophp.php 第 100-130 行

$_SERVER['starttime'] = $starttime;
$_SERVER['time'] = $time;
$_SERVER['ip'] = $ip;
$_SERVER['longip'] = $longip;
$_SERVER['useragent'] = $useragent;
$_SERVER['conf'] = $conf;
$_SERVER['lang'] = $lang;
$_SERVER['errno'] = $errno;
$_SERVER['errstr'] = $errstr;
$_SERVER['method'] = $method;
$_SERVER['ajax'] = $ajax;
$_SERVER['get_magic_quotes_gpc'] = $get_magic_quotes_gpc;
// ...
$db = !empty($conf['db']) ? db_new($conf['db']) : NULL;
$conf['cache']['mysql']['db'] = $db;
$cache = !empty($conf['cache']) ? cache_new($conf['cache']) : NULL;
unset($conf['cache']['mysql']['db']);
$_SERVER['db'] = $db;
$_SERVER['cache'] = $cache;

2. db.func.php 函数读 $_SERVER['db'] 取依赖

文件:xiunobbs_4.0.4/xiunophp/db.func.php 第 49-58 行

function db_sql_find_one($sql, $d = NULL) {
    $db = $_SERVER['db'];                              // 从 $_SERVER 取依赖
    $d = $d ? $d : $db;
    if(!$d) return FALSE;
    $arr = $d->sql_find_one($sql);
    db_errno_errstr($arr, $d, $sql);
    return $arr;
}

文件:xiunobbs_4.0.4/xiunophp/db.func.php 第 28-37 行

function db_connect($d = NULL) {
    $db = $_SERVER['db'];                              // 双轨:global + $_SERVER
    $d = $d ? $d : $db;
    $r = $d->connect();
    db_errno_errstr($r, $d);
    return $r;
}

3. cache.func.php 同样读 $_SERVER['cache']

文件:xiunobbs_4.0.4/xiunophp/cache.func.php 第 23-33 行

function cache_get($k, $c = NULL) {
    $cache = $_SERVER['cache'];                        // 从 $_SERVER 取依赖
    $c = $c ? $c : $cache;
    if(!$c) return FALSE;
    strlen($k) > 32 AND $k = md5($k);
    $k = $c->cachepre.$k;
    $r = $c->get($k);
    return $r;
}

4. model 层函数读 global $conf 取配置(双轨制)

文件:xiunobbs_4.0.4/model/user.func.php 第 54-63 行

function user_update($uid, $arr) {
    global $conf, $g_static_users;                     // 从 global 取 $conf
    $r = user__update($uid, $arr);
    $conf['cache']['type'] != 'mysql' AND cache_delete("user-$uid");
    isset($g_static_users[$uid]) AND $g_static_users[$uid] = array_merge($g_static_users[$uid], $arr);
    return $r;
}

 misc.func.php  url() 函数却从 $_SERVER 取配置:

文件:xiunobbs_4.0.4/model/misc.func.php 第 14-17 行

function url($url, $extra = array()) {
    $conf = _SERVER('conf');                            // 从 $_SERVER 取 $conf
    !isset($conf['url_rewrite_on']) AND $conf['url_rewrite_on'] = 0;
    // ...
}

5. lang() 函数读 $_SERVER['lang']

文件:xiunobbs_4.0.4/xiunophp/misc.func.php 第 174-184 行

function lang($key, $arr = array()) {
    $lang = $_SERVER['lang'];                           // 从 $_SERVER 取语言包
    if(!isset($lang[$key])) return 'lang['.$key.']';
    $s = $lang[$key];
    if(!empty($arr)) {
        foreach($arr as $k=>$v) {
            $s = str_replace('{'.$k.'}', $v, $s);
        }
    }
    return $s;
}

6. 无 Container 类、无 register/resolve/get 等容器接口

Grep 全站无 ContainerDIregisterresolvemake 等容器相关关键字(除 cache_new/db_new 工厂函数)。所有依赖获取均为隐式 global  $_SERVER

7. 无 mock 能力,单元测试无法隔离依赖

业务函数内部直接读 $_SERVER['db'],测试时无法替换为 mock 对象。只能通过 $_SERVER['db'] = $mock 全局替换,污染其他测试用例。

风险等级与结论

架构缺陷(严重)

危害:

  • $_SERVER 超全局变量被滥用为容器,违反 PHP 语言设计意图($_SERVER 应仅存服务器环境信息)
  • 同一依赖(如 $conf)通过 global  $_SERVER 双轨暴露,命名空间混乱
  • 无 mock 能力,单元测试无法隔离数据库/缓存依赖,业务代码不可测试
  • 多实例场景(如同时连接两个数据库)只能通过 $d 参数手动传递,无容器管理生命周期
  • 依赖创建与使用耦合,db_new/cache_new  xiunophp.php 启动时硬编码实例化,无法延迟加载
  • 无依赖注入接口,第三方插件无法替换核心依赖(如自定义 cache 实现)
  • 与"全局变量泛滥"缺陷叠加,函数副作用完全不可控

修复建议:

  • 引入轻量 DI 容器(如 class Container { static function get($name) {...} }
  • 核心依赖通过 Container::register('db', function() {...}) 注册,Container::get('db') 获取
  • 业务函数通过参数接收依赖(依赖注入),而非内部 global/$_SERVER 读取
  • 支持 mock:测试时 Container::set('db', $mockDb) 替换
  • 支持延迟加载:db 仅在首次 Container::get('db') 时实例化
  • 废弃 $_SERVER['db']/$_SERVER['cache']/$_SERVER['conf'] 等用法,统一走容器
最新回复 (0)
全部楼主
返回