URL重定向及跳转漏洞

URL跳转漏洞

   URL 跳转漏洞是指后台服务器在告知浏览器跳转时,未对客户端传入的重定向地址进行合法性校验,导致用户浏览器跳转到钓鱼页面的一种漏洞。

使用场景

   现在 Web 登录很多都接入了QQ、微信、新浪等第三方登录,以 QQ 第三方授权登录为例说明,在我们调用 QQ 授权服务器进行授权时,会在参

数中传入redirect_url(重定向)地址,告知 QQ 授权服务器,授权成功之后页面跳转到这个地址,然后进行站点登录操作。但是如果你的重定向地址在

传输过程中被篡改成了一个钓鱼网址,那么就是导致用户的授权信息被非法获取。当然,QQ 第三方登录,也会有自己的策略,就是接入 QQ 第三方

登录的应用,会在开发者平台,配置相关的跳转白名单,只有属于白名单中的域名、子域名或 url ,QQ授权服务器才跳转,如果发现 redirect_url 不

合法,则跳转到非法页面。

防御策略

   根据上面的场景分析,我们知道,之所以会出现跳转 URL 漏洞,就是因为服务端没有对客户端传递的跳转地址进行合法性校验,所以,预防这种攻

击的方式,就是对客户端传递过来的跳转 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);

  

posted @ 2018-11-14 15:44  流光瞬息  阅读(11922)  评论(0编辑  收藏  举报