此文章为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; // hook model_user_token_gen_start.php $tokenkey = md5(xn_key()); $token = xn_encrypt("$ip $time $uid", $tokenkey); // hook model_user_token_gen_end.php 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 字节。