利用PHP进行反向代理

利用PHP进行反向代理

本文是本站之前发布的博客文章,讲述的利用php来进行反向代理服务器的过程。本文所建立的反向代理只有本人一人使用且已于毕设结束后彻底销毁,未访问任何国内法律法规禁止的网站。

前端时间一直在完成毕设,中间也断断续续写了几篇博客。在完成毕设过程中,必不可少的就是查找资料。对我来说,使用最多的就是谷歌学术和Github了,但是由于某些原因,国内访问这俩个网站那都是得费一番功夫。谷歌学术目前应该是被国内彻底给ban了,Github的话用校园网也是时而连得上时而连不上…

2024-03-19T18:11:40.png
要说谷歌学术这个东西吧,其实学校的在线图书馆也买了资源,直接用校园ip访问相应网站就直接免费阅读、下载,但是github吧……很多时候写代码还得参考一下。

一开始使用的是魔法,每次要访问时候都一堆流程:点击图标->等待检测系统->选择地区->连接ing->连接成功。这还是运气好的情况,要是运气不好,在等待检测系统那个阶段直接告诉你系统有问题,又得关掉魔法的后台,重新打开,再走一遍流程……只能说是烦的要命。

突然想到我之前在几个主机网站都白嫖了几个VPS,而且地区都是在国外!这么一想,要是我去访问这个VPS,然后这个VPS代替我去访问目标网站,之后将结果再返回给我这不就OK了?细想一下好像确实可以这么做,但是这算不算是私自搭建国际信道呢?

《计算机信息网络国际联网管理暂行规定实施办法》
第七条 我国境内的计算机信息网络直接进行国际联网,必须使用邮电部国家公用电信网提供的国际出入口信道。 任何单位和个人不得自行建立或者使用其他信道进行国际联网。
第二十二条 违反本办法第七条和第十条第一款规定的,由公安机关责令停止联网,可以并处一万五千元以下罚款;有违法所得的,没收违法所得。

整个流程是这样的:小明有个海外服务器,以及一个域名A。现在小明想要访问一个国内无法访问的网站B。域名A对应的服务器是在国外,服务器上面不存在有任何违法内容。在个人访问时,通过国内的合法信道访问这个服务器,并告诉这个服务器需要网站B(网站B在国内无法访问),之后这个服务器去访问网站B,网站B的内容再通过服务器返回给小明。

理论上来说,小明使用的就是公用电信网提供的出口信道,所以应该是没有犯法的。实际操作中这个情况不一定会被如何处理,所以……先不去研究小明了,先去看看如何实现吧?


以下内容仅个人测试使用,实现完成后所有内容均已删除,并未传播或代理访问任何非法网站。如您使用下面代码部署并做任何事情均与本站无关!请一定要遵守当地的法律法规!


目前在github上面搜索PHP反代还是有很多代码的,很多也有十分详细的教程。这里使用到的俩个代码也来自那。

一、Any Proxy

这个作者的代码是相当的简洁,其实后端部分我倒是没怎么改动,因为也不太会PHP。主要是对作者源代码里面的前端部分进行了修改,使最终不会出现问题。在配置时候只要把这个文档放到网站的根目录上,让命名成为index.php就可以实现初步的访问了,PHP部分的代码如下:

<?php
$pass = "test";//test为默认密码,自行修噶
if (isset($_POST['Any-Proxy'])) {
    setcookie("Any-Proxy", $_POST['Any-Proxy'], time()+3600*24*366);
    header("Refresh:0");
}
if ($_COOKIE['Any-Proxy'] != $pass) {
    header('HTTP/1.1 403');
    exit('<!DOCTYPE html><html><head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, initial-scale=1.0"><style>form{width:90%;margin:0 auto;text-align:center}</style></head><body><meta charset="UTF-8"><form method="post">密码:<input type="password"name="Any-Proxy"/><input type="submit"value="访问"/></form></body></html>');
}
$host = $_SERVER['HTTP_HOST'];
$path = $_SERVER['REQUEST_URI'];
$https = ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on") || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == "https")) ? "https://" : "http://";
//$anyip值为1发送服务器IP头,值为2则发送随机IP,值为3发送客户端IP,仅在部分网站中有效
$anyip = 1;
if (substr($path, -2) == "~q") {
    del_cookie();
    header("Location: " . $https . $host);
    exit;
}
if (substr($path, 1, 7) == "http://" || substr($path, 1, 8) == "https://" || $_POST['urlss']) {
    if ($_POST['urlss']) {
        $url = $_POST['urlss'];
    } else {
        $url = substr($path, 1);
    }
    if (substr($url, 0, 4) != "http") {
        $url = "http://" . $url;
    }
    $PageUrl = parse_url($url);
    $PageUrl['query'] ? $query = "?" . $PageUrl['query'] : $query = "";
    $http = $PageUrl['scheme'] . "://";
    $PageUrls = $https . $host . $PageUrl['path'] . $query;
    del_cookie();
    setcookie("urlss", $http . $PageUrl['host'], 0, "/");
    header("Location: " . $PageUrls);
    exit;
} elseif (!$_COOKIE['urlss']) {
    exit('<html><head><meta charset="utf-8"><meta name="viewport" content="width=520, user-scalable=no, target-densitydpi=device-dpi"><title>代理访问_Any-Proxy</title><link rel="stylesheet" type="text/css" href="//s0.pstatp.com/cdn/expire-1-M/bootswatch/3.4.0/paper/bootstrap.min.css"><style type="text/css">.row{margin-top:100px}.page-header{margin-bottom:90px}.expand-transition{margin-top:150px;-webkit-transition:all.5s ease;transition:all.5s ease}</style></head><body><div id="app" class="container"><div class="row row-xs"><div class="col-lg-6 col-md-6 col-sm-6 col-xs-10 col-xs-offset-1 col-sm-offset-3 col-md-offset-3 col-lg-offset-3"><div class="page-header"><h3 class="text-center h3-xs">Any-Proxy</h3></div><form method="post"><div class="form-group " id="input-wrap"><label class="control-label" for="inputContent">请输入需访问的链接:</label><input type="text" id="inputContent" class="form-control" name="urlss" placeholder="http://" required="required"></div><div class="text-right"><input type="submit" class="input_group_addon btn btn-primary" value="GO"></div></div></form></div></div><div align="center" class="expand-transition"><p>在当前链接末尾输入 ~q 可以退出当前页面回到首页</p><p>在域名后面加上链接地址即可访问,如 ' . $https . $host . '/http://ip38.com/ </p></div></div><footer class="footer navbar-fixed-bottom" style="text-align:center"><div class="container"><p>请勿访问您当地法律所禁止的网页,否则后果自负。</p><p>©Powered by <a href="https://github.com/yitd/Any-Proxy">Any-Proxy</a></p></div></footer></body></html>');
}
//代理的域名及使用的协议最后不用加/
$target_host = $_COOKIE['urlss'];
if (substr($target_host, 0, 4) != "http") {
    $target_host = "http://" . $target_host;
}
//处理代理的主机得到协议和主机名称
$protocal_host = parse_url($target_host);
//以.分割域名字符串
$rootdomain = explode(".",$host);
//获取数组的长度
$lenth = count($rootdomain);
//获取顶级域名
$top = ".".$rootdomain[$lenth-1];
//获取主域名
$root = ".".$rootdomain[$lenth-2];
//判断请求的域名或ip是否合法
if (strstr($target_host, ".") === false || $protocal_host['host'] == $host) {
    del_cookie();
    echo "<script>alert('请求的域名有误!');window.location.href='" . $https . $host . "';</script>";
    exit;
}
$PageIP = gethostbyname($protocal_host['host']);
if (filter_var($PageIP, FILTER_VALIDATE_IP)) {
    if (filter_var($PageIP, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false) {
        del_cookie();
        echo "<script>alert('请求的ip被禁止!');window.location.href='" . $https . $host . "';</script>";
        exit;
    }
} else {
    del_cookie();
    echo "<script>alert('请求的域名有误!');window.location.href='" . $https . $host . "';</script>";
    exit;
}
// set URL and other appropriate options
$aAccess = curl_init();
curl_setopt($aAccess, CURLOPT_URL, $protocal_host['scheme'] . "://" . $protocal_host['host'] . $path);
curl_setopt($aAccess, CURLOPT_HEADER, true);
curl_setopt($aAccess, CURLOPT_RETURNTRANSFER, true);
curl_setopt($aAccess, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($aAccess, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($aAccess, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($aAccess, CURLOPT_TIMEOUT, 10);
curl_setopt($aAccess, CURLOPT_BINARYTRANSFER, true);
//关系数组转换成字符串,每个键值对中间用=连接,以; 分割
function array_to_str($array) {
    $string = "";
    if (is_array($array)) {
        foreach ($array as $key => $value) {
            if (!empty($string)) $string.= "; " . $key . "=" . $value;
            else $string.= $key . "=" . $value;
        }
    } else {
        $string = $array;
    }
    return urldecode($string);
}
if ($_SERVER['HTTP_REFERER']) {
    $referer = str_replace($host, $protocal_host['host'], $_SERVER['HTTP_REFERER']);
}
if ($anyip == "1") {
    $remoteip = $_SERVER['HTTP_CLIENT_IP'];
} elseif ($anyip == "2") {
    $remoteip = mt_rand(1,255).".".mt_rand(1,255).".".mt_rand(1,255).".".mt_rand(1,255);
} elseif (empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
    $remoteip = $_SERVER['REMOTE_ADDR'];
} else {
    $remoteip = $_SERVER['HTTP_X_FORWARDED_FOR'];
}
//$headers = get_client_header();
$headers = array();
$headers[] = "Accept-language: " . $_SERVER['HTTP_ACCEPT_LANGUAGE'];
$headers[] = "Referer: " . $referer;
$headers[] = "CLIENT-IP: " . $remoteip;
$headers[] = "X-FORWARDED-FOR: " . $remoteip;
$headers[] = "Cookie: " . array_to_str($_COOKIE);
$headers[] = "user-agent: " . $_SERVER['HTTP_USER_AGENT'];
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    $headers[] = "Content-Type: " . $_SERVER['CONTENT_TYPE'];
    curl_setopt($aAccess, CURLOPT_POST, 1);
    curl_setopt($aAccess, CURLOPT_POSTFIELDS, http_build_query($_POST));
}
curl_setopt($aAccess, CURLOPT_HTTPHEADER, $headers);
// grab URL and pass it to the browser
$sResponse = curl_exec($aAccess);
//判断请求url是否被重定向
$locurl = parse_url(curl_getinfo($aAccess, CURLINFO_EFFECTIVE_URL));
if ($locurl['scheme'] . "://" . $locurl['host'] != $protocal_host['scheme'] . "://" . $protocal_host['host']) {
    setcookie("urlss", $locurl['scheme'] . "://" . $locurl['host'], 0, "/");
}
list($headerstr, $sResponse) = parse_header($sResponse);
$headarr = explode("\r\n", $headerstr);
foreach ($headarr as $h) {
    if (strlen($h) > 0) {
        if (strpos($h, 'ETag') !== false) continue;
        if (strpos($h, 'Connection') !== false) continue;
        if (strpos($h, 'Cache-Control') !== false) continue;
        if (strpos($h, 'Content-Length') !== false) continue;
        if (strpos($h, 'Transfer-Encoding') !== false) continue;
        if (strpos($h, 'HTTP/1.1 100 Continue') !== false) continue;
        if (strpos($h, 'Strict-Transport-Security') !== false) continue;
        if (strpos($h, 'Set-Cookie') !== false) {
            $targetcookie = $h . ";";
            //如果返回到客户端cookie不正常可把下行中的$root . $top换成$host
            $res_cookie = preg_replace("/domain=.*?;/", "domain=" . $root . $top .";", $targetcookie);
            $h = substr($res_cookie, 0, strlen($res_cookie) - 1);
            header($h, false);
        } else {
            header($h);
        }
    }
}
function del_cookie() {
    foreach ($_COOKIE as $key => $value) {
        if ($key == "Any-Proxy") continue;
        setcookie($key, null, time() - 3600, "/");
    }
}
function get_client_header() {
    $headers = array();
    foreach ($_SERVER as $k => $v) {
        if (strpos($k, 'HTTP_') === 0) {
            $k = strtolower(preg_replace('/^HTTP/', '', $k));
            $k = preg_replace_callback('/_\w/', 'header_callback', $k);
            $k = preg_replace('/^_/', '', $k);
            $k = str_replace('_', '-', $k);
            if ($k == 'Host') continue;
            $headers[] = "$k: $v";
        }
    }
    return $headers;
}
function header_callback($str) {
    return strtoupper($str[0]);
}
function parse_header($sResponse) {
    list($headerstr, $sResponse) = explode("\r\n\r\n", $sResponse, 2);
    $ret = array($headerstr, $sResponse);
        if (preg_match('/^HTTP\/1\.1 \d{3}/', $sResponse)) {
            $ret = parse_header($sResponse);
        }
    return $ret;
}
//解决中文乱码
$charlen = stripos($sResponse, "charset");
if (stristr(substr($sResponse, $charlen, 18) , "GBK") || stristr(substr($sResponse, $charlen, 18) , "GB2312")) {
    $sResponse = mb_convert_encoding($sResponse, "UTF-8", "GBK,GB2312,BIG5");
}
header("Pragma: no-cache");
// close cURL resource, and free up system resources
$sResponse = str_replace("http://" . $protocal_host['host'], $https . $host, $sResponse);
$sResponse = str_replace("https://" . $protocal_host['host'], $https . $host, $sResponse);
curl_close($aAccess);
echo $sResponse;

在修改完index.php文件后,必要的就是在服务器上开启伪静态。主要有Apache和nginx俩种类型的,我的VPS就是Apache的所以设置伪静态的代码如下:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^/(.*)$ /index.php?$1 [L]

将这个命名成.htaccess文件即可
如果你是nginx服务器,你的伪静态设置代码就如下所示:

if ( !-e $request_filename) {
       rewrite ^/(.*)$ /index.php?$1 last;
       break;
}

至此,就完成了网站的所有编写,你在绑定域名之后访问就可以看到啦~
输入对应的网址就可以进行访问了!当然如果要是直接输入你的网址+待访问网址也是可以直接进行反代访问的。

二、Reverse Proxy

上面介绍的那个是可以代理访问任何网站的,下面介绍的这个是只能代理访问某个特定网站的,适合用于固定访问的,可以给访问者查看的。代码也是相当的简洁啊!

<?php
//代理的域名及使用的协议最后不用加/
$target_host="https://scholar.google.com";
//处理代理的主机得到协议和主机名称
$protocal_host=parse_url($target_host);
//解决中文乱码
#header("Content-Type:text/html;charset=gb2312"); 
//获取浏览器的的Agent
$Agent = $_SERVER['HTTP_USER_AGENT'];
//以.分割域名字符串
$rootdomain=explode(".",$_SERVER["SERVER_NAME"]);
//获取数组的长度
$lenth=count($rootdomain);
//获取顶级域名
$top=".".$rootdomain[$lenth-1];
//获取主域名
$root=".".$rootdomain[$lenth-2];

//解析url参数
function get_request_params() 
{ 
   $url = $_SERVER["REQUEST_URI"]; 
   
   $refer_url = parse_url($url); 
   
   $params = $refer_url['query']; 
   
   $arr = array(); 
   if(!empty($params)) 
   { 
       $paramsArr = explode('&',$params); 
   
       foreach($paramsArr as $k=>$v) 
       { 
          $a = explode('=',$v); 
          $arr[$a[0]] = $a[1]; 
       } 
   } 
   return $arr; 
}
//解析HTTP响应头
function parse_headers($headers)
{
    //$head = array();
    global $root,$top;
    foreach( $headers as $k=>$v )
    {
        $t = explode( ':', $v, 2 );
        if( isset( $t[1] ) )
        {
            //$head[ trim($t[0]) ] = trim( $t[1] );
            if(strcasecmp('Set-Cookie',trim($t[0]))==0)
            {
                //处理COOkie的domain关键字
                $targetcookie=trim( $t[1] ).";";
                $res_cookie=preg_replace("/domain=.*?;/","domain=".$root.$top.";",$targetcookie);
                $res_cookie=substr($res_cookie,0,strlen($res_cookie)-1); 
                header("Set-Cookie: ".$res_cookie);
            }
            elseif(strcasecmp('Content-Type',trim($t[0]))==0)
            {
                header("Content-Type: ".trim( $t[1] ));
            }
            elseif(strcasecmp('Location',trim( $t[0] ))==0)
            {
                $relocation=str_replace($protocal_host['host'],$_SERVER["SERVER_NAME"],trim( $t[1] ));
                header("Location: ".$relocation);
            }
            elseif(strcasecmp('cache-control',trim( $t[0] ))==0)
            
                header("cache-control: ".trim( $t[1] ));
                
            else
                continue;
        }
        // else
        // {
            // $head[] = $v;
            // if( preg_match( "#HTTP/[0-9\.]+\s+([0-9]+)#",$v, $out ) )
                // $head['reponse_code'] = intval($out[1]);
        // }
    }
    return;// $head;
}
//关系数组转换成字符串,每个键值对中间用=连接,以; 分割
function array_to_str($array)  
{  
   $string="";
    if (is_array($array)) 
    {  
        foreach ($array as $key => $value) 
        {   
            if(!empty($string))
                $string.="; ".$key."=".$value;
            else
                $string.=$key."=".$value;
        }   
    } else 
    {  
            $string = $array;  
    }      
    return $string;  
}  
//debug to console
function debug_to_console($data) {
    if(is_array($data) || is_object($data))
    {
        echo("<script>console.log('PHP: ".json_encode($data)."');</script>");
    } else {
        echo("<script>console.log('PHP: ".$data."');</script>");
    }
}
//组装HTTP请求头
$opts="";
if($_SERVER['REQUEST_METHOD']=='POST') 
{
    $postdata=file_get_contents("php://input");
    $opts = array(
    'http'=>array(
    'method'=>$_SERVER['REQUEST_METHOD'],
    'content'=>$postdata,
    'header'=>"Accept-language: zh-CN\r\n" .   //可以使用客户端浏览器的$_SERVER['HTTP_ACCEPT_LANGUAGE']
              "user-agent: '$Agent'"."\r\n".
              "Cookie: ".array_to_str($_COOKIE)."\r\n".
              //"Accept-Encoding: gzip, deflate, sdch\r\n".
              "Content-Type: ".$_SERVER['CONTENT_TYPE']
              
        )
    );
    
}
else
{
    $opts = array(
    'http'=>array(
    'method'=>$_SERVER['REQUEST_METHOD'],
    'header'=>"Accept-language: zh-CN\r\n" .
              "user-agent: '$Agent'"."\r\n".
              //"Accept-Encoding: gzip, deflate, sdch\r\n".
              "Cookie: ".array_to_str($_COOKIE)
        )
    );
    
}
//
$context = stream_context_create($opts);
//发送请求
$new_request_uri = "";
$path_script  = pathinfo($_SERVER["PHP_SELF"]);
//
if ($path_script['dirname']!="/") {
    $new_request_uri = substr_replace($_SERVER["REQUEST_URI"],"",strpos($_SERVER["REQUEST_URI"],$path_script['dirname']),strlen($path_script['dirname']));
} else {
    $new_request_uri = $_SERVER["REQUEST_URI"];
}
$homepage = file_get_contents($protocal_host['scheme']."://".$protocal_host['host'].$new_request_uri,false,$context);
//处理file_get_contents返回的响应求头
parse_headers($http_response_header);
//替换域名并输出网页
$homepage=str_replace($protocal_host['host'],$_SERVER["SERVER_NAME"],$homepage);
//输出网页内容
echo $homepage;
?>

上面代码已经加了注释,应该是很好理解的。然后这个代理的话默认指向的网站是谷歌,如果需要反代其他网站的话就直接在第一行中修改一下就OK了。之后还有一个就是配置一下伪静态了,和之前的一样就不赘述啦!

好了,本次分享的俩个反代就到这里了。
你完全可以在国内的主机上面搭建个反代用来作为匿名访问国内可以访问的网站,反正实际体验相当顺滑!


本文首发于:Mister.Hu的世界

Comments

No comments yet. Why don’t you start the discussion?

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注