Referer校验

Referer是http协议中定义的,Referer就是上一个页面的地址,这个是浏览器会在点击一个链接时自动添加到请求头中的

 

查看一个request信息:

GET /adjs.php?n=348893119&what=zone:382&charset=utf-8&exclude=,&referer=http%3A//topic.csdn.net/u/20100730/16/fd41a3a3-8976-49ce-b32e-65011241e626.html HTTP/1.1
Host: z.csdn.net
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8
Accept: */*
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Referer: http://topic.csdn.net/u/t5/include/ad1.asp?pdate=2010-07-30%2016:47:51&ba=Java&sa=J2EE

最下面就是referer的值.

那referer有什么用呢? 
1,防止盗连,比如我是个下载软件的网站,在下载页面我先用referer来判断上一页面是不是自己网站,如果不是,说明有人盗连了你的下载地址。 
2,电子商务网站的安全,我在提交信用卡等重要信息的页面用referer来判断上一页是不是自己的网站,如果不是,可能是黑客用自己写的一个表单,来提交,为了能跳过你上一页里的javascript的验证等目的。 
3,统计,比如从我主页上链接到一个朋友那里,他的服务器就能够从HTTP Referer中统计出每天有多少用户点击我主页上的链接访问他的网站

防盗链代码:

 public void doFilter(ServletRequest req, ServletResponse res,  
            FilterChain chain) throws ServletException, IOException {  

        HttpServletRequest request = (HttpServletRequest) req;  
        HttpServletResponse response = (HttpServletResponse) res;  
        // 禁止缓存  
        response.setHeader("Cache-Control", "no-store");  
        response.setHeader("Pragrma", "no-cache");  
        response.setDateHeader("Expires", 0);  
        // 链接来源地址  
        String referer = request.getHeader("referer");  
        System.out.println("refer is"+referer);  
        System.out.println("serverName is"+request.getServerName());  
        if (referer == null || !referer.contains(request.getServerName())) {  
        /** 
        * 如果 链接地址来自其他网站,则返回错误图片 
        */  
        request.getRequestDispatcher("/error.jsp").forward(request, response);  
        } else {  
        /** 
        * 图片正常显示 
        */  
        chain.doFilter(request, response);  
        }  
    }  
  • 不过referer不是安全的,在Java中获取一个网站的HTML内容可以通过HttpURLConnection来获取.我们在HttpURLConnection中可以设置referer来伪造referer,轻松绕过这类防采集的网站
HttpURLConnection conn = (HttpURLConnection) newURL(path).openConnection();  
conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible;MSIE 7.0; Windows NT 5.1; Maxthon;)");  
conn.setRequestProperty("Accept-Encoding", "gzip");  
conn.setRequestProperty("referer","http://control.blog.sina.com.cn/selienceblog");  
conn.setRequestProperty("cookie","http://control.blog.sina.com.cn");  
InputStream inputStream = conn.getInputStream();  
//保存inputstream中的东西就OK了

其实原理非常简单就是设置下请求头,通过设置user-agent让服务器识别我们的身份,上面的例子中,我们就告诉浏览器我是用的maxthon遨游浏览器.通过设置useragent大部分的防采集的网站可以通过了,第二中比较严格的防采集的网站是通过请求头的referer和cookie来判断的.使用jetty的HttpClient的可以通过ContentExchange的addRequestHeader来设置请求头

contentExchange.addRequestHeader("User-Agent",  
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1;Maxthon;)");  
contentExchange.addRequestHeader("Accept-Encoding", "gzip");  
contentExchange.addRequestHeader("referer","http://control.blog.sina.com.cn/selienceblog");  
contentExchange.addRequestHeader("cookie", "data");  
contentExchange.setMethod("GET");  

下面内容转自:http://blog.csdn.net/zitian886/article/details/46630085

1 、CSRF攻击原理 
CSRF攻击原理比较简单,如图1所示。其中Web A为存在CSRF漏洞的网站,Web B为攻击者构建的恶意网站,User C为Web A网站的合法用户。

  1. 用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A; 
    2.在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;
  2. 用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;
  3. 网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;
  4. 浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。 
    2、 CSRF漏洞防御 
    CSRF漏洞防御主要可以从三个层面进行,即服务端的防御、用户端的防御和安全设备的防御。 
    2.1 服务端的防御 
    2.1.1 验证HTTP Referer字段 
    根据HTTP协议,在HTTP头中有一个字段叫Referer,它记录了该HTTP请求的来源地址。在通常情况下,访问一个安全受限页面的请求必须来自于同一个网站。比如某银行的转账是通过用户访问http://bank.test/test?page=10&userID=101&money=10000页面完成,用户必须先登录bank.test,然后通过点击页面上的按钮来触发转账事件。当用户提交请求时,该转账请求的Referer值就会是转账按钮所在页面的URL(本例中,通常是以bank. test域名开头的地址)。而如果攻击者要对银行网站实施CSRF攻击,他只能在自己的网站构造请求,当用户通过攻击者的网站发送请求到银行时,该请求的Referer是指向攻击者的网站。因此,要防御CSRF攻击,银行网站只需要对于每一个转账请求验证其Referer值,如果是以bank. test开头的域名,则说明该请求是来自银行网站自己的请求,是合法的。如果Referer是其他网站的话,就有可能是CSRF攻击,则拒绝该请求。 
    2.1.2 在请求地址中添加token并验证 
    CSRF攻击之所以能够成功,是因为攻击者可以伪造用户的请求,该请求中所有的用户验证信息都存在于Cookie中,因此攻击者可以在不知道这些验证信息的情况下直接利用用户自己的Cookie来通过安全验证。由此可知,抵御CSRF攻击的关键在于:在请求中放入攻击者所不能伪造的信息,并且该信息不存在于Cookie之中。鉴于此,系统开发者可以在HTTP请求中以参数的形式加入一个随机产生的token,并在服务器端建立一个拦截器来验证这个token,如果请求中没有token或者token内容不正确,则认为可能是CSRF攻击而拒绝该请求。 
    2.1.3 在HTTP头中自定义属性并验证 
    自定义属性的方法也是使用token并进行验证,和前一种方法不同的是,这里并不是把token以参数的形式置于HTTP请求之中,而是把它放到HTTP头中自定义的属性里。通过XMLHttpRequest这个类,可以一次性给所有该类请求加上csrftoken这个HTTP头属性,并把token值放入其中。这样解决了前一种方法在请求中加入token的不便,同时,通过这个类请求的地址不会被记录到浏览器的地址栏,也不用担心token会通过Referer泄露到其他网站。 
    3 其他防御方法
  5. CSRF攻击是有条件的,当用户访问恶意链接时,认证的cookie仍然有效,所以当用户关闭页面时要及时清除认证cookie,对支持TAB模式(新标签打开网页)的浏览器尤为重要。
  6. 尽量少用或不要用request()类变量,获取参数指定request.form()还是request. querystring (),这样有利于阻止CSRF漏洞攻击,此方法只不能完全防御CSRF攻击,只是一定程度上增加了攻击的难度。

Java 代码示例 
下文将以 Java 为例,对上述三种方法分别用代码进行示例。无论使用何种方法,在服务器端的拦截器必不可少,它将负责检查到来的请求是否符合要求,然后视结果而决定是否继续请求或者丢弃。在 Java 中,拦截器是由 Filter 来实现的。我们可以编写一个 Filter,并在 web.xml 中对其进行配置,使其对于访问所有需要 CSRF 保护的资源的请求进行拦截。 
在 filter 中对请求的 Referer 验证代码如下 
清单 1. 在 Filter 中验证 Referer 
// 从 HTTP 头中取得 Referer 值 
String referer=request.getHeader(“Referer”); 
// 判断 Referer 是否以 bank.example 开头 
if((referer!=null) &&(referer.trim().startsWith(“bank.example”))){ 
chain.doFilter(request, response); 
}else{ 
request.getRequestDispatcher(“error.jsp”).forward(request,response); 
}  
以上代码先取得 Referer 值,然后进行判断,当其非空并以 bank.example 开头时,则继续请求,否则的话可能是 CSRF 攻击,转到 error.jsp 页面。 
如果要进一步验证请求中的 token 值,代码如下 
清单 2. 在 filter 中验证请求中的 token 
HttpServletRequest req = (HttpServletRequest)request; 
HttpSession s = req.getSession(); 
// 从 session 中得到 csrftoken 属性 
String sToken = (String)s.getAttribute(“csrftoken”); 
if(sToken == null){ 
// 产生新的 token 放入 session 中 
sToken = generateToken(); 
s.setAttribute(“csrftoken”,sToken); 
chain.doFilter(request, response); 
} else{ 
// 从 HTTP 头中取得 csrftoken 
String xhrToken = req.getHeader(“csrftoken”); 
// 从请求参数中取得 csrftoken 
String pToken = req.getParameter(“csrftoken”); 
if(sToken != null && xhrToken != null && sToken.equals(xhrToken)){ 
chain.doFilter(request, response); 
}else if(sToken != null && pToken != null && sToken.equals(pToken)){ 
chain.doFilter(request, response); 
}else{ 
request.getRequestDispatcher(“error.jsp”).forward(request,response); 

}  
首先判断 session 中有没有 csrftoken,如果没有,则认为是第一次访问,session 是新建立的,这时生成一个新的 token,放于 session 之中,并继续执行请求。如果 session 中已经有 csrftoken,则说明用户已经与服务器之间建立了一个活跃的 session,这时要看这个请求中有没有同时附带这个 token,由于请求可能来自于常规的访问或是 XMLHttpRequest 异步访问,我们分别尝试从请求中获取 csrftoken 参数以及从 HTTP 头中获取 csrftoken 自定义属性并与 session 中的值进行比较,只要有一个地方带有有效 token,就判定请求合法,可以继续执行,否则就转到错误页面。生成 token 有很多种方法,任何的随机算法都可以使用,Java 的 UUID 类也是一个不错的选择。 
除了在服务器端利用 filter 来验证 token 的值以外,我们还需要在客户端给每个请求附加上这个 token,这是利用 js 来给 html 中的链接和表单请求地址附加 csrftoken 代码,其中已定义 token 为全局变量,其值可以从 session 中得到。 
清单 3. 在客户端对于请求附加 token 
function appendToken(){ 
updateForms(); 
updateTags(); 
}

function updateForms() { 
// 得到页面中所有的 form 元素 
var forms = document.getElementsByTagName(‘form’); 
for(i=0; i

猜你喜欢

转载自blog.csdn.net/luzhangtong/article/details/82025268