关于weiphp的openid外链分享的严重BUG

weiphp微信开发框架存在这样一个问题,当用户分享某个页面到好友、朋友圈时会附加上自身的openid(openid是微信公众号来识别用户的唯一ID),甚至当其他用户点击链接访问时,框架以为是前者的用户身份,而且没有校验。这点BUG无论对于业务还是安全性来说都影响非常大。

在此简单做一说明及修复方案,框架版本:2.0,3.0某些版本也存在。

首先是业务逻辑方面,例如官方附带的插件,投票、填表等。

首先通过关键词触发Vote插件  .../Vote/WeixinAddonModel.class.php 

Vote插件返回给客户端一个图文链接,其中的地址包含了当前用户的OpenId。(这里如果当用户分享地址给其他人,则其他人也会以前者的身份登录)

###文件地址:/Addons/Vote/Model/WeixinAddonModel.class.php
###从代码可以看出,URL是这么来的。其中官方的备注是必须传输openid,否则无法辨认来源用户身份。
###在这里说明下,此处个人建议写法是依然传输token,也就是公众号id。
###该框架是针对多公众号的,否则无法指定所服务的公众号。
###主要将openid屏蔽掉,不要进行传输。

// 以下官方写法

	//组装用户在微信里点击图文的时跳转URL
	//其中token和openid这两个参数一定要传,否则程序不知道是哪个微信用户进入了系统
	$param ['id'] = $info ['id'];
	$param ['token'] = get_token ();
	$param ['openid'] = get_openid ();
	$url = addons_url ( 'Vote://Vote/show', $param );


//以下修改后

	//组装用户在微信里点击图文的时跳转URL
	//其中token和openid这两个参数一定要传,否则程序不知道是哪个微信用户进入了系统
	$param ['id'] = $info ['id'];
	$param ['token'] = get_token ();
	//$param ['openid'] = get_openid ();
	$url = addons_url ( 'Vote://Vote/show', $param );

以上做法也只是保住了一部分,毕竟框架功能基本已经完成了,其他地方也均要修改,也是一件比较麻烦的事情。

建议直接修改addons_url 函数,屏蔽掉构造参数中的openid。

/Application/Common/Common/function.php

###文件地址:/Application/Common/Common/function.php

/**
 * 插件显示内容里生成访问插件的url
 * @param string $url url
 * @param array $param 参数
 * @author ....
 */
function addons_url($url, $param = array()) {

     ...

	/* 解析URL带的参数 */
	if (isset ( $url ['query'] )) {
		parse_str ( $url ['query'], $query );
		$param = array_merge ( $query, $param );
	}
	
	/* 基础参数 */
	$params = array (
			'_addons' => ucfirst ( $addons ),
			'_controller' => ucfirst ( $controller ),
			'_action' => $action 
	);
	$params = array_merge ( $params, $param ); // 添加额外参数
    
    //增加过滤openid
	if(isset($param['openid'])) unset($param['openid']);

	return U ( 'Home/Addons/execute', $params );
}

自此之后,openid貌似已经过滤完了,但经测试发现,用户首次访问公众号网页的时候,会进行oauth2授权获取openid,此时链接地址上,会再次出现openid。

###文件地址:/Application/Common/Common/function.php

###原因在此
function OAuthWeixin($callback) {
	$isWeixinBrowser = isWeixinBrowser ();
	$info = get_token_appinfo ();
	if (! $isWeixinBrowser || $info ['type'] != 2 || empty ( $info ['appid'] )) {
		redirect ( $callback . '&openid=-1' );
	}
	$param ['appid'] = $info ['appid'];
	
	if (! isset ( $_GET ['getOpenId'] )) {
		$param ['redirect_uri'] = $callback . '&getOpenId=1';
		$param ['response_type'] = 'code';
		$param ['scope'] = 'snsapi_base';
		$param ['state'] = 123;
		$url = 'https://open.weixin.qq.com/connect/oauth2/authorize?' . http_build_query ( $param ) . '#wechat_redirect';
		redirect ( $url );
	} elseif ($_GET ['state']) {
		$param ['secret'] = $info ['secret'];
		$param ['code'] = I ( 'code' );
		$param ['grant_type'] = 'authorization_code';
		
		$url = 'https://api.weixin.qq.com/sns/oauth2/access_token?' . http_build_query ( $param );
		$content = file_get_contents ( $url );
		$content = json_decode ( $content, true );
        //获取到用户openid之后,openid又被附加到URL上,进行跳转了。
		redirect ( $callback . '&openid=' . $content ['openid'] );
	}
}

做一修改,将openid不再附加到URL上,而是直接写到session里面。

###文件地址:/Application/Common/Common/function.php

function OAuthWeixin($callback) {
	$isWeixinBrowser = isWeixinBrowser ();
	$info = get_token_appinfo ();
	if (! $isWeixinBrowser || $info ['type'] != 2 || empty ( $info ['appid'] )) {
		redirect ( $callback . '&openid=-1' );
	}
	$param ['appid'] = $info ['appid'];
	
	if (! isset ( $_GET ['getOpenId'] )) {
		$param ['redirect_uri'] = $callback . '&getOpenId=1';
		$param ['response_type'] = 'code';
		$param ['scope'] = 'snsapi_base';
		$param ['state'] = 123;
		$url = 'https://open.weixin.qq.com/connect/oauth2/authorize?' . http_build_query ( $param ) . '#wechat_redirect';
		
		redirect ( $url );
	} elseif ($_GET ['state']) {
		$param ['secret'] = $info ['secret'];
		$param ['code'] = I ( 'code' );
		$param ['grant_type'] = 'authorization_code';
		
		$url = 'https://api.weixin.qq.com/sns/oauth2/access_token?' . http_build_query ( $param );
		$content = file_get_contents ( $url );
		$content = json_decode ( $content, true );
		//此处增加将openid写入session
		if($content['openid']!==NULL){
			get_openid($content ['openid']);
		}
		//过滤url中敏感参数
		$callback = filterUrlParam($callback);
		redirect ( $callback);
	}
} 

//过滤URL参数
function filterUrlParam($url,$filter = array('getOpenId','code','state','openid')){
	$parse_param = parse_url($url);
	$query_array = explode('&',$parse_param['query']);
	$query = '';
	foreach($query_array as $val){
		$tmp = explode('=', $val);
		if(count($tmp)>=1){
			!in_array($tmp[0], $filter) && $query .= $val.'&';
		}
	}
	$parse_param['query'] = $query = rtrim($query,'&');
	$url = unparse_url($parse_param);
	return $url;
}
//以上所需函数
function unparse_url($parsed_url) { 
  $scheme   = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : ''; 
  $host     = isset($parsed_url['host']) ? $parsed_url['host'] : ''; 
  $port     = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : ''; 
  $user     = isset($parsed_url['user']) ? $parsed_url['user'] : ''; 
  $pass     = isset($parsed_url['pass']) ? ':' . $parsed_url['pass']  : ''; 
  $pass     = ($user || $pass) ? "$pass@" : ''; 
  $path     = isset($parsed_url['path']) ? $parsed_url['path'] : ''; 
  $query    = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : ''; 
  $fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : ''; 
  return "$scheme$user$pass$host$port$path$query$fragment"; 
}

之后,还有进行处理的一处就是将URL参数中openid识别为真实用户的罪魁祸首。

###文件地址:/Application/Common/Common/function.php

// 获取当前用户的OpenId
function get_openid($openid = NULL) {
	$token = get_token ();
	if ($openid !== NULL) {
		session ( 'openid_' . $token, $openid );
	} 
//删除掉参数请求中的openid
//  elseif (! empty ( $_REQUEST ['openid'] )) {
//		session ( 'openid_' . $token, $_REQUEST ['openid'] );
//	}
	$openid = session ( 'openid_' . $token );
	
	$isWeixinBrowser = isWeixinBrowser ();
	if (empty ( $openid ) && $isWeixinBrowser) {
		$callback = GetCurUrl ();
		OAuthWeixin ( $callback );
	}
	
	if (empty ( $openid )) {
		return - 1;
	}
	
	return $openid;
}

好一番折腾,终于将openid过滤完了。

以上均为个人观点,如果有存在错误或者有疑问请在文末留言,谢谢!。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏http://www.cnblogs.com

php7安装redis拓展

phpredis下载地址https://github.com/phpredis/phpredis 解压并进入源码包 unzip phpredis-develop...

354120
来自专栏http://www.cnblogs.com

centos安装phpmyadmin

1.准备工作: 修改php的配置文件php.ini session.save_path = “/var/lib/php/session” 添加目录: mkd...

620110
来自专栏我的博客

Composer

Composer是PHP中用来管理依赖(dependency)关系的工具。你可以在自己的项目中声明所依赖的外部工具库(libraries),Composer会帮...

42170
来自专栏我的博客

php使用elasticsearch

1.引入包 composer require elasticsearch/elasticsearch 2.DEMO参考 <?php require_once ...

48770
来自专栏杂七杂八

phpstudy升级mysql5.7以及遇到的问题汇总

最近学习java的时候建数据库,用到了create_time和update_time,我想设置成current_time,但是在mysql5,7之前貌似不支持...

45070
来自专栏http://www.cnblogs.com

centos-6.5安装部署LNMP环境

安装部署前,确保安装了gcc和gcc-c++ 系统信息: [root@zww ~]# cat /etc/redhat-release CentOS releas...

398110
来自专栏我的博客

PHP取余的那些事

1、百分号取余 $val=9.45; $result=$val*100; echo intval($result); //这里输出944 echo $re...

437100
来自专栏我的博客

PHP反射机制

PHP反射机制它是用来导出或提取出关于类、方法、属性(私有保护等属性也能获取)、参数等的详细信息,包括注释。 <?php class Test{ ...

44470
来自专栏狂码一生

PHP $_SERVER大全详解

$_SERVER['HTTP_ACCEPT_LANGUAGE']//浏览器语言  $_SERVER['REMOTE_ADDR'] //当前用户 IP 。  $_...

393120
来自专栏杂七杂八

phpstudy本地创建站点

打开站点域名管理 ? 填写站点管理内容,然后点击新增,左侧会增加更才填写的内容,然后点击保存设置并生产配置文件 修改hosts本地映射 ? ? Paste_Im...

36260

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励