URL跳轉漏洞php
URL 跳轉漏洞是指後臺服務器在告知瀏覽器跳轉時,未對客戶端傳入的重定向地址進行合法性校驗,致使用戶瀏覽器跳轉到釣魚頁面的一種漏洞。瀏覽器
使用場景服務器
如今 Web 登陸不少都接入了QQ、微信、新浪等第三方登陸,以 QQ 第三方受權登陸爲例說明,在咱們調用 QQ 受權服務器進行受權時,會在參微信
數中傳入redirect_url(重定向)地址,告知 QQ 受權服務器,受權成功以後頁面跳轉到這個地址,而後進行站點登陸操做。可是若是你的重定向地址在dom
傳輸過程當中被篡改爲了一個釣魚網址,那麼就是致使用戶的受權信息被非法獲取。固然,QQ 第三方登陸,也會有本身的策略,就是接入 QQ 第三方url
登陸的應用,會在開發者平臺,配置相關的跳轉白名單,只有屬於白名單中的域名、子域名或 url ,QQ受權服務器才跳轉,若是發現 redirect_url 不code
合法,則跳轉到非法頁面。blog
防護策略ip
根據上面的場景分析,咱們知道,之因此會出現跳轉 URL 漏洞,就是由於服務端沒有對客戶端傳遞的跳轉地址進行合法性校驗,因此,預防這種攻ci
擊的方式,就是對客戶端傳遞過來的跳轉 URL 進行校驗。
經常使用的方式:
服務端配置跳轉白名單或域名白名單,只對合法的 URL 作跳轉
下面是關於PHP服務端對客戶端傳遞過來的跳轉 URL 進行校驗的代碼:
<?php // $allowedDomains 表示容許跳轉的url白名單 $allowedDomains = array( "aaaa.com" "bbbb.com" ....... ); function encodeUrl($urlInfo) {/*{{{*/ $path = isset($urlInfo['path']) ? $urlInfo['path'] : ''; if(!empty($path)) { $t = explode("/", $path); for($i = 0; $i < count($t); $i++) { $t[$i] = rawurlencode($t[$i]); } $path = implode("/", $t); } $query = isset($urlInfo['query']) ? $urlInfo['query'] : ''; if(!empty($query)) { $t = explode("&", $query); for($i = 0; $i < count($t); $i++) { $tt = explode("=", $t[$i]); $tt[1] = rawurlencode($tt[1]); $t[$i] = implode("=", $tt); } $query = implode("&", $t); } if(!isset($urlInfo['host']) || empty($urlInfo['host'])) { return $path. "?". $query; } $scheme = isset($urlInfo['scheme']) ? $urlInfo['scheme'] : 'http'; $port = isset($urlInfo['port']) ? $urlInfo['port'] : 80; $request = $scheme . '://'. $urlInfo['host']; $request .= ($port == 80) ? '' : ':'.$port; $request .= $path; $request .= (empty($query)) ? '' : '?'.$query; return $request; }/*}}}*/ function checkUrl($url,$domainArr=array()) {/*{{{*/ $res = array('isTrustedDomain' => false,'url' => '','domain' => ''); if(empty($url)) return $res; $domainArr = empty($domainArr) || !is_array($domainArr) ? $allowedDomains : $domainArr; $url = filterUrl($url);//先過濾特殊字符 $p = parse_url($url); $scheme = $p['scheme']; if(!in_array(strtolower($scheme),array('http','https'))){ return $res; } $host = $p['host']; if(!isValidHost($host)){ return $res; } $hostLen = strlen($host); foreach($domainArr as $domain){ $firstPos = strpos($host, $domain); if($firstPos !== false && ($firstPos + strlen($domain)) == $hostLen){ if($firstPos == 0 || $domain[0] == '.' || $host[$firstPos-1] == '.'){ $res['isTrustedDomain'] = true; $res['url'] = $url; $res['domain'] = $domain; break; } } } return $res; }/*}}}*/ function filterUrl( $url ) {/*{{{*/ if(empty($url)) return $url; // Strip all of the Javascript in script tags out... $url = preg_replace('/<SCRIPT.*?<\/SCRIPT>/ims',"",$url); // Strip all blank character $url = preg_replace('/[\s\v\0]+/',"",$url); //Strip special characters(',",<,>,\) $url = str_replace(array("'","\"","<",">","\\"),'',$url); return $url; }/*}}}*/ function isValidHost($host) {/*{{{*/ $p = "/^[0-9a-zA-Z\-\.]+$/"; return preg_match($p,$host) ? true : false; }/*}}}*/ $url = "https://www.baidu.com"; $call_back_url = trim($url); $call_back_url = encodeUrl(parse_url(urldecode($call_back_url))); $res = checkUrl($call_back_url, $domainArr); var_dump($res);