weiphp微信开发框架存在这样一个问题,当用户分享某个页面到好友、朋友圈时会附加上自身的openid(openid是微信公众号来识别用户的惟一ID),甚至当其余用户点击连接访问时,框架觉得是前者的用户身份,并且没有校验。这点BUG不管对于业务仍是安全性来讲都影响很是大。php
在此简单作一说明及修复方案,框架版本:2.0,3.0某些版本也存在。json
首先是业务逻辑方面,例如官方附带的插件,投票、填表等。api
首先经过关键词触发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 );
以上作法也只是保住了一部分,毕竟框架功能基本已经完成了,其余地方也均要修改,也是一件比较麻烦的事情。session
建议直接修改addons_url 函数,屏蔽掉构造参数中的openid。微信开发
/Application/Common/Common/function.phpapp
###文件地址:/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过滤完了。
以上均为我的观点,若是有存在错误或者有疑问请在文末留言,谢谢!。