此文章为XIUNOX版本重构审计时发现问题,XIUNOX版本已优化修复此问题。分享出来方便后续想基于xiuno bbs4.0.4版本制作维护版本或插件模板等需求的开发者和站长参考。
问题:XXTEA 弱加密算法用于 Cookie/Token 加密,密钥派生仅 md5
现象
Xiuno BBS 4.0.4 的核心加密函数 xn_encrypt/xn_decrypt 在未加载 xiuno.so 扩展时,回退到纯 PHP 实现的 XXTEA 算法。XXTEA 是 1998 年设计的分组加密算法,并非密码学标准推荐算法(对比 AES),存在以下问题:
- 算法本身非现代标准:XXTEA 未经 NIST/ISO 密码学评审,无 AEAD(无完整性/认证标签),密文可被篡改后仍解密为有效结构(无 MAC 校验)。
- 密钥派生弱:
xn_key() 直接返回 $conf['auth_key'] 字符串,无 KDF(PBKDF2/HKDF/Argon2);xxtea_encrypt 内部将 key 字符串 str2long 转 4 个 32 位整数,不足补 0,密钥有效熵可能远低于预期。
- 密文无完整性校验:
xn_decrypt 解密后直接返回,无 HMAC/签名验证,攻击者可构造/篡改密文(如 bbs_admin_token、bbs_token)进行比特翻转/选择密文攻击。
- 影响面广:该函数被用于加密
bbs_token(用户登录态)、bbs_admin_token(后台登录态)、xn_safe_key 等,一旦算法被攻破或密钥泄露,所有依赖它的鉴权令牌可被伪造。
源码证据
证据 1:xn_encrypt/xn_decrypt 回退到 XXTEA,无完整性校验 文件:xiunobbs_4.0.4/xiunophp/xn_encrypt.func.php 行 37-48
function xn_encrypt($txt, $key = '') {
empty($key) AND $key = xn_key();
$encrypt = function_exists('xiuno_encrypt') ? xiuno_encrypt($txt, $key) : xxtea_encrypt($txt, $key);
return xn_urlencode(base64_encode($encrypt));
}
function xn_decrypt($txt, $key = '') {
empty($key) AND $key = xn_key();
$encrypt = base64_decode(xn_urldecode($txt));
$ret = function_exists('xiuno_decrypt') ? xiuno_decrypt($encrypt, $key) : xxtea_decrypt($encrypt, $key);
return $ret;
}
第 39 行:未加载 xiuno.so 时回退到 xxtea_encrypt;第 46 行解密后直接 return $ret,无 HMAC/签名校验。xn_encrypt 输出 base64_encode(xxtea_encrypt(plain, key)),无 IV、无认证标签。
证据 2:XXTEA 纯 PHP 实现,key 派生仅 str2long 补零 文件:xiunobbs_4.0.4/xiunophp/xn_encrypt.func.php 行 97-126
function xxtea_encrypt($str, $key) {
if($str == '') return '';
$v = xxtea_str2long($str, TRUE);
$k = xxtea_str2long($key, FALSE);
if (count($k) < 4) {
for ($i = count($k); $i < 4; i++) {
$k[$i] = 0;
}
}
$n = count($v) - 1;
$z = $v[$n];
$y = $v[0];
$delta = 0x9E3779B9;
$q = floor(6 + 52 / ($n + 1));
$sum = 0;
while (0 < $q--) {
$sum = xxtea_int32($sum + $delta);
$e = $sum >> 2 & 3;
for ($p = 0; $p < $n; $p++) {
$y = $v[$p + 1];
$mx = xxtea_int32((($z >> 5 & 0x07ffffff) ^ $y << 2) + (($y >> 3 & 0x1fffffff) ^ $z << 4)) ^ xxtea_int32(($sum ^ $y) + ($k[$p & 3 ^ $e] ^ $z));
$z = $v[$p] = xxtea_int32($v[$p] + $mx);
}
}
return xxtea_long2str($v, FALSE);
}
第 100-105 行:$k = xxtea_str2long($key, FALSE),key 不足 4 个 32 位整数时补 0($k[$i] = 0),短密钥的有效熵被零填充稀释;无 KDF,密钥直接作为算法输入。
证据 3:xn_key 直接返回 auth_key 字符串,无 KDF 文件:xiunobbs_4.0.4/xiunophp/xn_encrypt.func.php 行 16-19
function xn_key($fromso = TRUE) {
$conf = _SERVER('conf');
return ($fromso && function_exists('xiuno_key')) ? xiuno_key() : (isset($conf['auth_key']) ? $conf['auth_key'] : '');
}
第 18 行:未加载扩展时直接返回 $conf['auth_key'] 字符串作为加密密钥,无 PBKDF2/HKDF 拉伸。该 auth_key 在默认配置中为硬编码值(见独立审计文件),密钥强度进一步降低。
证据 4:XXTEA 加密用于鉴权令牌 文件:xiunobbs_4.0.4/model/user.func.php 行 348-358
function user_token_gen($uid) {
global $ip, $time, $conf;
$tokenkey = md5(xn_key());
$token = xn_encrypt("$ip $time $uid", $tokenkey);
return $token;
}
第 353-354 行:bbs_token 内容 $ip\t$time\t$uid 经 xn_encrypt(XXTEA)加密。bbs_admin_token(admin.func.php 行 58)同样用 xn_encrypt。若 XXTEA 密文可被篡改/伪造,攻击者可构造任意 uid 的有效令牌,实现身份伪造。
风险等级与结论
风险等级:中高危
结论:
- 核心加密采用 XXTEA(非现代标准、无 AEAD、无完整性校验),纯 PHP 回退实现,密钥派生仅
str2long 补零,无 KDF。
xn_decrypt 解密后无 HMAC/签名校验,密文可被篡改;结合明文结构已知($ip\t$time\t$uid),存在选择密文/比特翻转攻击空间。
- 该加密保护
bbs_token/bbs_admin_token 等鉴权令牌,算法弱点直接危及身份认证体系。
auth_key 默认硬编码(见独立审计文件),密钥强度问题叠加,进一步降低伪造令牌难度。
- 修复建议:改用
openssl_encrypt/sodium_crypto_secretbox(AES-256-GCM 或 XSalsa20-Poly1305)等现代 AEAD 算法;密钥派生使用 HKDF/PBKDF2;密文包含随机 IV 与认证标签;解密后校验 MAC;auth_key 强制随机生成且不少于 32 字节。