php网页代理实现原理笔记

去年冬天我说过要改一改phproxy让他能够正常浏览twitter和facebook。到现在马上要一年过去了我还没动静。。作出承诺而不执行实在糟糕。这里先做个笔记希望接下来能继续做下去。假如有同学愿意帮助我的话则极其欢迎!!

这里用访问网页的流程作为引子说明一下php网页代理的简单原理。
首先用户提交一个url给服务端。这里有个trick就是这个url以name为q的一个GET方法传入服务端。后面这个trick有很有效的应用。
服务端接到请求后读取这个url,需要做url的parse。这个在php里有现成的函数parse_url。需要解析的东西是很多的。比如要知道他是http还是https。要拆分出主机名和后面的路径。假如有端口号要记录下来,否则http赋以80端口,https赋以443端口。等等。
分析完成之后需要建立连接,然后发送请求。建立连接使用的是fsockopen。之后需要根据http协议建立请求。http协议其实是用\r\n分隔的一系列文本行。要说明的东西很多,比如GET还是POST方法,访问域名和路径是什么,cookie和session定义等等涉及到方方面面非常细节的东西。我还没有完全看完,不过如果只是想要读取一个简单的页面的话只需最基本的几个项目定义好就可以了。将这些内容做成一个字符串,然后使用fwrite写入到前面打开的socket中去。
发送请求之后就使用fread读取socket等待响应。需要用一个while循环不断读取,因为他可能一次发送不完。前面发送的东西是一个http头,跟上面提到的内容一样也是一系列\r\n分隔的文本项。需要根据协议依次处理这些内容。我在实验中完全忽略了这些内容只是将后面的网页显示出来。但phproxy的代码里面已经将处理http头的工作做得非常细致了。
理论上来说读取到的网页信息(也就是<html>标签包含的那一堆东西)只需直接echo出来就可以在页面上显示了。做到这里最原始的一个网页代理就做完了。但事实上这是远远不够的。首先发现的就是很多链接无法打开,css和js文件没能加载,很多图片也显示不了。为什么呢,因为他们使用了相对路径,而现在的主机域名已经变成phproxy架设的域名了。即使他们使用了完整路径,但是由于不经过代理的缘故客户端很可能还是无法加载。
因此必须将那些资源依次下载到服务器上,并将路径改成服务器上的路径,才能使用户正确的读取。
这里就用到了最开始的那个trick了。最佩服作者的地方就在这里。直接遍历下载下来的html脚本代码,找出所有可能出现网络路径的位置,例如src=啦,url(啦,background-image啦等等。并直接在这些路径前面加上phproxy路径加?q=就可以了。也就是本来

http://twitter.com/api/

这个路径就变成

http://phproxy.host.net/index.php?q=http://twitter.com/api/

这个形式。从而递归的调用了自己,将网站需要的资源全部下载下来。

当然这个简述简化了太多细节,尤其是http头的处理部分。但那些并不是我关心的。我关心的东西主要有两个。一个是为什么他不能访问facebook,一个是他为什么打开twitter之后无法发推(新版twitter只能显示标题栏)。

不能访问facebook原因很简单,问题出在用户通过form发送请求时使用的是明码传输。即使使用了base64编码也无济于事,因为那并不是一个加密算法。必须在用户输入url地址后通过javascript在客户端加密之后发送到服务端才能解决问题。这很简单,我昨晚尝试了一下,仅仅使用了每个字符的ascii码减1这种方式,就轻松穿过了防火墙。而服务端发回的数据$_response_body也使用某种简单算法加密,然后urlencoding之后放到一段javascript代码里面。

<script language="javascript">
function my_decode(body){...}
document.write(my_decode(decodeURIComponent("$_response_body")));
</script>

上面这个是拼接完的效果省略了很多单引号和字符串连接。当是伪代码来看吧。
这样客户端接收到数据后就会先解密然后显示出来。加密算法本身没有必要很复杂,只用最弱智的加密就可以。至少目前是这样。需要注意的是$_response_body里面的东西必须urlencoding,否则它里面有双引号啥的的话就会把整个javascript毁掉了。

但后续的工作并不轻松。因为前文提到整个网页中的全部资源都必须重新下载,因此必须修改代码中每一处可能用到url的地方,调试工作还需要一些时间。这两天正好是秋学期期末考试,考完试的空闲时间应该可以搞定。

另外一个问题是他为什么能上推但是不能发推。原因很简单那就是他还不支持ajax。要让他支持ajax稍微有点难度。当然说起来很容易那就是去parse 全部javascript代码,发现ajax的code就给他加上一前述proxy前缀就好了。但问题没那么简单。javascript实在太灵活了,任何地方都可能出现url。换句话说,要想完成这个功能要做javascript的语义分析。我想说那个工作的复杂度已经远远超出做网页代理的范围了。当然什么时候有空可以搜搜看有没有开源的js分析工具。但我不清楚有没有php版本的。假如有js版本的也可以,嵌一段js code到网页里在客户端解析。但无论如何是个大工程。至少对目前的我来说是。(假如有人愿意帮忙的话非常欢迎~~~)

如果totally放弃这个思路换一个想法呢?能不能在客户端发起ajax请求的时候去劫持他到另外一个服务器呢?那当然难度就简化多了,但那就不是一个网页代理可以做的事情了。需要我们自己写一个浏览器才行。。但,只是再多想一步,是不是做一个浏览器插件可以解决问题呢?说不定真可以!可惜我没有做浏览器插件的经验,目前还不好说。假如哪位同学有兴趣欢迎讨论~~

以上

2 comments on “php网页代理实现原理笔记

  • 吓死我了刚才写了一个js 的alert居然就真跳对话框了。吓得我赶紧去装了过滤插件。
    不过后来才发先以管理员身份登录的时候他不会检查内容。。

    呜。。果然还是挺吓人啊。。真被XSS了岂不是。。|||
    幸亏本人庸碌无为也不会有人来X我。。

Comments are closed.