使用 ThinkPHP 官方 WeChat 包時,普通/兼容模式正常,但**安全模式**解密失敗。經對比官方樣例代碼與 WechatCrypt.class.php,邏輯無誤,通過 file_put_contents 落盤分析解密後明文,發現 XML 格式不標準導致 simplexml_load_string 解析失敗。
解密後的 XML 字符串包含轉義字符與換行:
<xml>
<ToUserName><![CDATA[gh_249aeb986d99]]><\/ToUserName>\n
<FromUserName><![CDATA[oopVmxHZaeQkDPsRcbpwXKkH-J2Q]]><\/FromUserName>\n
<CreateTime>1448944621<\/CreateTime>\n
<MsgType><![CDATA[text]]><\/MsgType>\n
<Content><![CDATA[\u7ecf\u7406]]><\/Content>\n
<MsgId>6223169761311044588<\/MsgId>\n
<\/xml>
關鍵問題:
<\/Tag> 應為 </Tag>(反斜杠多餘)>\n 應為 >(多餘換行)\u7ecf\u7406 未解碼(simplexml 可處理,但建議預處理)WechatCrypt::decrypt() 返回的明文未做後處理,直接傳給 simplexml_load_string。官方示例中明文本即標準 XML,但實際騰訊服務器返回的加密數據解密後含轉義字符。
在 WechatCrypt.class.php 的 decrypt() 方法末尾,返回明文前添加清洗邏輯:
// 明文內容
$text = substr($decrypt, 4, $size);
// 【修復】清洗非標準 XML 轉義
$text = str_replace('<\/', '</', $text); // 修正閉合標籤
$text = str_replace('>\n', '>', $text); // 移除標籤後換行
return $text;
public function decrypt($encrypt) {
$encrypt = base64_decode($encrypt);
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
mcrypt_generic_init($td, $this->cyptKey, substr($this->cyptKey, 0, 16));
$decrypt = mdecrypt_generic($td, $encrypt);
$decrypt = self::PKCS7Decode($decrypt, mcrypt_enc_get_key_size($td));
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
if (strlen($decrypt) < 16) throw new \Exception("非法密文字符串!");
$decrypt = substr($decrypt, 16); // 去隨機串
$size = unpack("N", substr($decrypt, 0, 4));
$size = $size[1];
$appid = substr($decrypt, $size + 4);
if ($appid !== $this->appId) throw new \Exception("非法APP_ID!");
$text = substr($decrypt, 4, $size);
// 關鍵修復
$text = str_replace('<\/', '</', $text);
$text = str_replace('>\n', '>', $text);
return $text;
}
修復後,安全模式下 simplexml_load_string 正常解析,消息處理流程恢復正常。