PHP伪造来源HTTP_REFERER的方法讨论
可以用于 防止恶意的顶贴,限制同一时间内重复发贴
如今网络上十分流行论坛自动发帖机,自动顶贴机等,给众多论坛网站带来了大量的垃圾信息,许多网站只是简单地采用了判断HTTP_REFERER的值来进行过滤机器发帖,可是网页的HTTP_REFERER来路信息是可以被伪造的。任何事物都是双面刃,只要你善于利用就有其存在价值。
很早以前,下载软件如Flashget,迅雷等都可以伪造来路信息了,而这些软件的伪造HTTP_REFERER大多是基于底层的sock来构造虚假的http头信息来达到目的。本文就纯粹从技术角度讨论一下,php语言下的伪造HTTP_REFERER的方法,以期让大家了解过程,更好的防御。
环境:Apache/2.2.8 + PHP/5.2.5 + Windows XP系统,本地测试。
首先,在网站虚拟根目录下建立1.php和2.php两个文件。
其中,1.php文件内容如下:
$host = '127.0.0.1';
$target = '/2.php';
$referer = 'http://www.piaoyi.org'; //伪造HTTP_REFERER地址
$fp = fsockopen($host, 80, $errno, $errstr, 30);
if (!$fp){
echo "$errstr($errno)<br />\n";
}
else{
$out = "
GET $target HTTP/1.1
Host: $host
Referer: $referer
Connection: Close\r\n\r\n";
fwrite($fp, $out);
while (!feof($fp)){
echo fgets($fp, 1024);
}
fclose($fp);
}
?>
另一个2.php文件很简单,只是写上一行读取当前的HTTP_REFERER服务器值的代码即可,如下:
echo "<hr />";
echo $_SERVER["HTTP_REFERER"];
?>
执行1.php文件,打开http://localhost/1.php,页面返回信息如下:
HTTP/1.1 200 OK Date: Fri, 04 Apr 2008 16:07:54 GMT Server: Apache/2.2.8 (Win32) PHP/5.2.5 X-Powered-By: PHP/5.2.5 Content-Length: 27 Connection: close Content-Type: text/html; charset=gb2312
--------------------------------------------------------------------------------
http://www.piaoyi.org
看到了结果了吧,伪造来源HTTP_REFERER信息成功。所以,如果你的网站仅仅是判断HTTP_REFERER,并不是安全的,别人一样可以构造这样的来源,简单的防御方法就是验证页里加上验证码;还可以结合IP判断的方法。
补充:ASP下的伪造来源的代码如下:
set http=server.createobject("MSXML2.XMLHTTP") '//MSXML2.serverXMLHTTP也可以
Http.open "GET",url,false
Http.setRequestHeader "Referer","http://www.piaoyi.org/"
Http.send() %>
如果你是一个有心人,请不要恶意利用这些方法,毕竟坏事做多了的话,效果就过犹不及了;比如你发大量的垃圾帖子吧,可能短期内会给你带来大量的外部链接,但这样的黑帽手段迟早要被搜索引擎发现,而这些已经发出去的链接就好像泼出去的水一样收不回来,这样的罪证就不是你能控制的了。
Javascript截取URL中的域名
通过正则获取,兼容ftp://username:password@格式
var getHostName=function(url) { //scheme : // [username [: password] @] hostame [: port] [/ [path] [? query] [# fragment]]*/ var e = new RegExp('^(?:(?:https?|ftp):)/*(?:[^@]+@)?([^:/#]+)'), matches = e.exec(url); return matches ? matches[1] : url; };
JSP获取header信息request列表
<%
out.println("Protocol: " + request.getProtocol() + "<br>");
out.println("Scheme: " + request.getScheme() + "<br>");
out.println("Server Name: " + request.getServerName() + "<br>" );
out.println("Server Port: " + request.getServERPort() + "<br>");
out.println("Protocol: " + request.getProtocol() + "<br>");
out.println("Server Info: " + getServletConfig().getServletContext().getServerInfo() + "<br>");
out.println("Remote Addr: " + request.getRemoteAddr() + "<br>");
out.println("Remote Host: " + request.getRemoteHost() + "<br>");
out.println("Character Encoding: " + request.getCharacterEncoding() + "<br>");
out.println("Content Length: " + request.getContentLength() + "<br>");
out.println("Content Type: "+ request.getContentType() + "<br>");
out.println("Auth Type: " + request.getAuthType() + "<br>");
out.println("HTTP Method: " + request.getMethod() + "<br>");
out.println("Path Info: " + request.getPathInfo() + "<br>");
out.println("Path Trans: " + request.getPathTranslated() + "<br>");
out.println("Query String: " + request.getQueryString() + "<br>");
out.println("Remote User: " + request.getRemoteUser() + "<br>");
out.println("Session Id: " + request.getRequestedSessionId() + "<br>");
out.println("Request URI: " + request.getRequestURI() + "<br>");
out.println("Servlet Path: " + request.getServletPath() + "<br>");
out.println("Accept: " + request.getHeader("Accept") + "<br>");
out.println("Host: " + request.getHeader("Host") + "<br>");
out.println("Referer : " + request.getHeader("Referer") + "<br>");
out.println("Accept-Language : " + request.getHeader("Accept-Language") + "<br>");
out.println("Accept-Encoding : " + request.getHeader("Accept-Encoding") + "<br>");
out.println("User-Agent : " + request.getHeader("User-Agent") + "<br>");
out.println("Connection : " + request.getHeader("Connection") + "<br>");
out.println("Cookie : " + request.getHeader("Cookie") + "<br>");
out.println("Created : " + session.getCreationTime() + "<br>");
out.println("LastAccessed : " + session.getLastAccessedTime() + "<br>");
%>
运行结果:
Protocol: HTTP/1.1
Scheme: http
Server Name: 192.168.0.1
Server Port: 8080
Protocol: HTTP/1.1
Server Info: JavaServer Web Dev Kit/1.0 EA (JSP 1.0; Servlet 2.1; Java 1.2; Windows NT 5.0 x86; java.vendor=Sun Microsystems Inc.)
Remote Addr: 192.168.0.106
Remote Host: abc
Character Encoding: null
Content Length: -1
Content Type: null
Auth Type: null
HTTP Method: GET
Path Info: null
Path Trans: null
Query String: null
Remote User: null
Session Id: To1010mC466113890241879At
Request URI: /c.jsp
Servlet Path: /c.jsp
Accept: */*
Host: 192.168.0.1:8080
Referer : null
Accept-Language : zh-cn
Accept-Encoding : gzip, deflate
User-Agent : Mozilla/4.0 (compatible; MSIE 5.0; Windows 98; DigExt)
Connection : Keep-Alive
Cookie : SESSIONID=To1010mC466113890241879At
Created : 965764522168
LastAccessed : 965775587088
在网站的统计数据中有对网站流量来路的统计,这些数据到底是怎么来的呢?这就要从浏览器如何访问一个网址说起了,简单点来说,当我们通过输入网址或者其他途径(点击网页中链接、地址收藏栏等)访问某个网址的时候,浏览器会向网页所在的web服务器发送http请求,web服务器在接受到请求后做相应的处理,并返回处理结果给客户端浏览器,如果一切正常的话,客户端(浏览器)就可以看到最终的网页效果了。
在客户端(浏览器)向web服务器发送请求(HTTP Request)的过程中会发送一些信息,这些信息叫做HTTP Header,web服务器会对HTTP Request做出回应,回应的消息叫做Response Header。流量统计中的网站来路和HTTP Header有关,在HTTP Header中可以包含HTTP Referer,在向服务器请求网页数据的时候服务器通过Referer就知道当前是从哪个网页过来的了,用PHP和ASP我们也可以很容易的获取到Referer信息。
接下来我们通过wireshark抓取下数据包来验证一下,下面是直接在浏览器中输入知蚁博客的url地址进行访问的时候发送给web服务器的HTTP Header,这里没有Referer信息,因此也就无法进行来路的判断。
GET / HTTP/1.1
Host: www.letuknowit.com
User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:2.0) Gecko/20100101 Firefox/4.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.5
Accept-Encoding: gzip, deflate
Accept-Charset: GB2312,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Cookie: AJSTAT_ok_times=19;
这个是在卢松松的留言板中打开知蚁博客的时候发送给web服务器的HTTP Header,下面的红色部分就是Referer了,根据这个信息流量统计代码就能够知道这个流量是来自于卢松松的留言板,也就可以进行来路的评定了。
GET / HTTP/1.1
Host: www.letuknowit.com
User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:2.0) Gecko/20100101 Firefox/4.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.5
Accept-Encoding: gzip, deflate
Accept-Charset: GB2312,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Referer: http://www.lusongsong.com/guestbook/
Cookie:AJSTAT_ok_times=19;
来路统计并不一定准确,完全可以通过程序进行来路的伪造,不知道大家有没有被别人刷站的经历,知蚁博客在刚刚建立不久就经历过一次,当时查看访问数据的时候小高兴了一把,访问量飙升啊,但是仔细看看来路后我郁闷了,全部是来自于sogou的关于【xx宝】关键字的,相当的恶心啊!
这是一个很典型的伪造来路的例子,打开来路地址,其中根本就没我们的网站链接。遇到这种情况,封IP基本上是搞不定的,因为每个流量的来路虽然一样,但是IP地址都不一样,其实知道了原理之后这个问题也好解决,对所有来路进行检测,当检测到特定来路的时候直接返回或者跳转到其他地址。
不过,这样做只能是让其无法打开我们的网页而已,实际其和web服务器的交互已经全部完成,并且从web服务器返回了网页html代码,我们只是在网页显示之前进行处理,其实就是相当于在网页开头写上一句重定向的代码,这样统计数据中就不会有这些来路了。最好的办法还是在web服务器端进行配置来限制特定的来路。
使用jpcap开源java包。(http://netresearch.ics.uci.edu/kfujii/Jpcap/doc/index.html)
调用jni :wincap/libcap。(先配置jni)
采用arp欺骗技术(使用jpcap包可以发送arp reply pack,线程定时发送)。使本机作为路由数据包的中转站。
原来传输:路由-->host a
欺骗后:路由-->local host 再转发(转发时可以设置一定的延时使目标host网速较慢,如果有这个必要)-->host a;
同样host a发送请求时先到本机再到路由。
定时计算同一host地址(截获的数据包中有)的数据包合计大小。折合成kb流量,时时显示(单独线程)
同时可以显示host req或resp的ip(dns反向解析地址可以显示host当前访问的网站);
停止监听:发送正确的arp reply pack给每一个host。
结束arp 欺骗reply包的发送。
http://www.iteye.com/problems/63753