这两天又开始做微信公众平台的开发,发现一个很奇怪的现象,从微信客户端发送的消息有时候能被回应,有时不能回应,起初我以为是网络问题,换了几个服务器问题依旧,检查后发现原因居然是签名验证问题!!!

而SDK用的签名验证代码分明是微信官方开发文档里的Demo代码:

private function checkSignature()
{
        $signature = $_GET["signature"];
        $timestamp = $_GET["timestamp"];
        $nonce = $_GET["nonce"];	

	$token = TOKEN;
	$tmpArr = array($token, $timestamp, $nonce);
	sort($tmpArr);
	$tmpStr = implode( $tmpArr );
	$tmpStr = sha1( $tmpStr );

	if( $tmpStr == $signature ){
		return true;
	}else{
		return false;
	}
}

同时文档给出的加密/校验流程如下:

1. 将token、timestamp、nonce三个参数进行字典序排序
2. 将三个参数字符串拼接成一个字符串进行sha1加密
3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

问题出在sort($tmpArr) 排序这一步。php中的sort函数的第二个参数是sort_flags,PHP文档说明如下

可选的第二个参数 sort_flags 可以用以下值改变排序的行为:
排序类型标记:
1. SORT_REGULAR – 正常比较单元(不改变类型)
2. SORT_NUMERIC – 单元被作为数字来比较
3. SORT_STRING – 单元被作为字符串来比较
4. SORT_LOCALE_STRING – 根据当前的区域(locale)设置来把单元当作字符串比较,可以用 setlocale() 来改变。
5. SORT_NATURAL – 和 natsort() 类似对每个单元以“自然的顺序”对字符串进行排序。 PHP 5.4.0 中新增的。
6. SORT_FLAG_CASE – 能够与 SORT_STRING 或 SORT_NATURAL 合并(OR 位运算),不区分大小写排序字符串。

默认状态下为SORT_REGULAR ,而要将token、timestamp、nonce三个参数进行字典序排序实际上应该是SORT_STRING。

比如下面一个数组:

array (
  0 =>'weixin',
  1 =>'1393914919',
  2 =>'576856862',
)

//用SORT_REGULAR排序:
array (
  0 =>'576856862',
  1 =>'1393914919',
  2 =>'weixin',
)
//用SORT_STRING排序:
array (
  0 =>'1393914919',
  1 =>'576856862',
  2 =>'weixin',
)

SORT_REGULAR在比较整数时是按数字大小
而SORT_STRING在比较整数时则是按ASCI码
这个排序就不一样了,当然因为nounce是一串随机数字,所以也有可能出现这两种排序一致的情况.
比如这个:

array (
  0 =>'weixin',
  1 =>'1393915880',
  2 =>'1540126295',
)
//用SORT_REGULAR排序:
array (
  0 =>'1393915880',
  1 =>'1540126295',
  2 =>'weixin',
)
//用SORT_STRING排序:
array (
  0 =>'1393915880',
  1 =>'1540126295',
  2 =>'weixin',
)

所以服务端的响应有一定机率成功,一定机率失败。

实际上这种情况是最近这几天才出现的,用以前的SDK开发之前一直没问题,最近腾讯应该改了服务端的代码,通知不发,官方文档DEMO代码不改,让那些还用原来SDK的开发者情何以堪!而签名验证这种基础层面的错误,目测是中招者一大片的节奏了。

本人测试公众号,欢迎关注:

我的微信公众号

我的微信公众号

- EOF -