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過濾完了。
以上均爲我的觀點,若是有存在錯誤或者有疑問請在文末留言,謝謝!。