此文章为XIUNOX版本重构审计时发现问题,XIUNOX版本已优化修复此问题。分享出来方便后续想基于xiuno bbs4.0.4版本制作维护版本或插件模板等需求的开发者和站长参考。
现象
Xiuno BBS 4.0.4 仅有 xn_log() 一个日志函数,全站仅 6 个 PHP 文件调用(统计 18 处),日志按月分目录存放于 log/YYYYMM/,每类日志一个 .php 文件(前缀 防止直接访问)。日志无级别字段(INFO/WARN/ERROR 混写)、无结构化字段(仅时间+IP+URL+UID+消息)、无日志切割(单文件无限增长)、无日志轮转/归档/清理策略、无远程日志收集。关键业务操作(登录、发帖、删除、支付)未强制记录日志,安全审计困难。
源码证据
1. xn_log 函数实现简陋,无级别、无结构化
文件:xiunobbs_4.0.4/xiunophp/misc.func.php 第 582-598 行
function xn_log($s, $file = 'error') {
if(DEBUG == 0 && strpos($file, 'error') === FALSE) return; // 线上仅记录含 error 的日志
$time = $_SERVER['time'];
$ip = $_SERVER['ip'];
$conf = _SERVER('conf');
$uid = intval(G('uid'));
$day = date('Ym', $time); // 按月分目录
$mtime = date('Y-m-d H:i:s');
$url = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
$logpath = $conf['log_path'].$day;
!is_dir($logpath) AND mkdir($logpath, 0777, true);
$s = str_replace(array("\r\n", "\n", "\t"), ' ', $s);
$s = "\t$mtime\t$ip\t$url\t$uid\t$s\r\n"; // 制表符分隔,无级别字段
@error_log($s, 3, $logpath."/$file.php"); // 单文件追加,无切割
}
2. xn_log 调用统计(Grep count)
xn_log → 18 处 / 6 个文件
分布:
xiunophp/misc.func.php5 处(含 error_handle 内部)xiunophp/db.func.php2 处(db_error)xiunophp/xiunophp.min.php7 处(合并文件,重复)admin/route/index.php2 处route/user.php1 处(仅 send_code 邮件失败)model/attach.func.php1 处
业务核心(route/post、route/thread、route/forum、route/mod、model/user、model/thread、model/post)零日志调用。
3. 关键流程无日志:登录成功/失败、发帖、删帖、用户注册均不记日志
文件:xiunobbs_4.0.4/route/user.php 第 84-102 行(登录)
md5($password.$_user['salt']) != $_user['password'] AND message('password', lang('password_incorrect'));
// 登录失败无 xn_log
user_update($_user['uid'], array('login_ip'=>$longip, 'login_date' =>$time , 'logins+'=>1));
$uid = $_user['uid'];
$_SESSION['uid'] = $uid;
user_token_set($_user['uid']);
// hook user_login_post_end.php
message(0, lang('user_login_successfully')); // 登录成功无 xn_log
文件:xiunobbs_4.0.4/route/post.php 第 209-218 行(删帖)
if($isfirst) {
thread_delete($tid); // 删主题无日志
} else {
post_delete($pid); // 删回帖无日志
}
// hook post_delete_end.php
message(0, lang('delete_successfully'));
4. modlog.func.php 存在但仅记录版主操作,且未在删帖流程强制调用
文件:xiunobbs_4.0.4/model/modlog.func.php(Grep 显示 1 处 global、28 处 hook,但 route/post.php 删帖流程未调用 modlog_create)。
5. 日志文件无切割,单文件无限增长
xn_log 使用 error_log($s, 3, $file) 追加模式,无文件大小检查、无按天切割、无日志保留期清理。线上长时间运行后单文件可达 GB 级,tail/grep 难以操作。
6. 日志目录权限 0777,存在安全隐患
!is_dir($logpath) AND mkdir($logpath, 0777, true);
log/ 目录权限 0777,任意用户可读写,多租户共享主机下可被其他用户篡改/读取日志中的 IP、URL、UID 信息。
风险等级与结论
架构缺陷(中)
危害:
- 业务核心(登录/发帖/删帖/注册)零日志,安全审计与攻击溯源无据可查
- 日志无级别字段,INFO/WARN/ERROR 混写,无法按级别过滤
- 无结构化(非 JSON),日志分析需正则解析,无法接入 ELK/Loki
- 单文件无限增长,无切割轮转,运维成本高
DEBUG=0时仅记录含 'error' 文件名的日志,业务日志被静默丢弃- 目录权限 0777,日志敏感信息(IP、URL、UID)可被同机其他用户读取
修复建议:
- 引入 PSR-3 Logger 接口,支持 INFO/WARN/ERROR/DEBUG 分级
- 日志格式改为 JSON Lines,字段包含
time,level,channel,uid,ip,url,trace_id,message,context - 关键业务操作(登录/发帖/删帖/支付/权限变更)强制记录审计日志
- 接入 Monolog,支持文件切割(按天/按大小)+ 远程 Syslog/ELK
- 日志目录权限改为 0750,敏感字段(IP)脱敏