前言

系统间进行请求访问,除了防火墙的校验,还可以通过下述方式进行简单的安全校验:

1、通过请求端的IP进行管控;

2、通过referer。

一、怎样正确设置remote_addr和x_forwarded_for

做网站时经常会用到remote_addr和x_forwarded_for这两个头信息来获取客户端的IP,然而当有反向代理或者CDN的情况下,这两个值就不够准确了,需要调整一些配置。

1、什么是remote_addr

remote_addr代表客户端的IP,但它的值不是由客户端提供的,而是服务端根据客户端的ip指定的,当你的浏览器访问某个网站时,假设中间没有任何代理,那么网站的web服务器(Nginx,Apache等)就会把remote_addr设为你的机器IP,如果你用了某个代理,那么你的浏览器会先访问这个代理,然后再由这个代理转发到网站,这样web服务器就会把remote_addr设为这台代理机器的IP。

2、什么是x_forwarded_for

正如上面所述,当你使用了代理时,web服务器就不知道你的真实IP了,为了避免这个情况,代理服务器通常会增加一个叫做x_forwarded_for的头信息,把连接它的客户端IP(即你的上网机器IP)加到这个头信息里,这样就能保证网站的web服务器能获取到真实IP。

2.1、使用HAProxy做反向代理

通常网站为了支撑更大的访问量,会增加很多web服务器,并在这些服务器前面增加一个反向代理(如HAProxy),它可以把负载均匀的分布到这些机器上。你的浏览器访问的首先是这台反向代理,它再把你的请求转发到后面的web服务器,这就使得web服务器会把remote_addr设为这台反向代理的IP,为了能让你的程序获取到真实的客户端IP,你需要给HAProxy增加以下配置

option forwardfor

它的作用就像上面说的,增加一个x_forwarded_for的头信息,把你上网机器的ip添加进去

2.2、使用Nginx的realip模块

当Nginx处在HAProxy后面时,就会把remote_addr设为HAProxy的IP,这个值其实是毫无意义的,你可以通过nginx的realip模块,让它使用x_forwarded_for里的值。使用这个模块需要重新编译Nginx,增加--with-http_realip_module参数

set_real_ip_from 10.1.10.0/24;
real_ip_header X-Forwarded-For;

上面的配置就是把从10.1.10这一网段过来的请求全部使用X-Forwarded-For里的头信息作为remote_addr

2.3、将Nginx架在HAProxy前面做HTTPS代理

网站为了安全考虑通常会使用https连接来传输敏感信息,https使用了ssl加密,HAProxy没法直接解析,所以要在HAProxy前面先架台Nginx解密,再转发到HAProxy做负载均衡。这样在Web服务器前面就存在了两个代理,为了能让它获取到真实的客户端IP,需要做以下配置。

首先要在Nginx的代理规则里设定

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

这样会让Nginx的https代理增加x_forwarded_for头信息,保存客户的真实IP。

其次修改HAProxy的配置

option forwardfor except 10.1.10.0/24

这个配置和之前设定的差不多,只是多了个内网的IP段,表示如果HAProxy收到的请求是由内网传过来的话(https代理机器),就不会设定x_forwarded_for的值,保证后面的web服务器拿到的就是前面https代理传过来的。

2.4、为什么PHP里的HTTP_X_FORWARDED_FOR和Nginx的不一样

当你的网站使用了CDN后,用户会先访问CDN,如果CDN没有缓存,则回源站(即你的反向代理)取数据。CDN在回源站时,会先添加x_forwarded_for头信息,保存用户的真实IP,而你的反向代理也会设定这个值,不过它不会覆盖,而是把CDN服务器的IP(即当前remote_addr)添加到x_forwarded_for的后面,这样x_forwarded_for里就会存在两个值。Nginx会使用这些值里的第一个,即客户的真实IP,而PHP则会使用第二个,即CDN的地址。为了能让PHP也使用第一个值,你需要添加以下fastcgi的配置。

fastcgi_param HTTP_X_FORWARDED_FOR $http_x_forwarded_for;

它会把nginx使用的值(即第一个IP)传给PHP,这样PHP拿到的x_forwarded_for里其实就只有一个值了,也就不会用第二个CDN的IP了。

2.5、忽略x_forwarded_for

其实,当你使用了Nginx的realip模块后,就已经保证了remote_addr里设定的就是客户端的真实IP,再看下这个配置

set_real_ip_from 10.1.10.0/24;
real_ip_header X-Forwarded-For;

它就是把x_forwarded_for设为remote_addr,而nginx里的x_forwarded_for取的就是其中第一个IP。

使用这些设置就能保证你的remote_addr里设定的一直都是客户端的真实IP,而x_forwarded_for则可以忽略了。

二、getRemoteAddr()和getRemoteHost() 区别

getRemoteAddr()是获得客户端的ip地址    

getRemoteHost()是获得客户端的主机名

获取客户端的IP地址的方法是:request.getRemoteAddr(),这种方法在大部分情况下都是有效的。但是在通过了Apache,Squid等反向代理软件就不能获取到客户端的真实IP地址了(比如负载均衡处理)。

public String getRemortIP(HttpServletRequest request) {  
  if (request.getHeader("x-forwarded-for") == null) { 
  return request.getRemoteAddr();  
//return request.getRemoteHost();
  }   
return request.getHeader("x-forwarded-for");
 }

此处,获取ip,当x-forwarded-for为null时,表示请求没有经过处理,此时调用getRemoteAddr()和getRemoteHost()都可获取真实ip

反之,则getHeader("x-forwarded-for")为真实的ip。

//produces = { "application/json;charset=UTF-8" }用以解决接受的中文乱码
    @RequestMapping(value = "getFlowTriangle",produces = { "application/json;charset=UTF-8" })
    @ResponseBody
    public String createFtgData(HttpServletRequest request) throws IOException, InterruptedException {
        System.out.println(request.getCharacterEncoding());
        String requestURL = request.getRequestURL().toString();
        String referer = request.getHeader("Referer");
        String remoteAddr = request.getRemoteAddr();
        String remoteHost = request.getRemoteHost();
        String forwarded = request.getHeader("x-forwarded-for");
        BufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream(),"utf-8"));
        String line = null;
        StringBuilder sb = new StringBuilder();
        while((line = br.readLine())!=null){
            sb.append(line);
        }
        JSONObject  jsonObject = JSONObject.parseObject(sb.toString());
        Object object = jsonObject.get("assessDate");
        String status = "{\"status\":\"生成成功\"}";
        return status;
    }

 

三、HTTP-referer防盗链安全辨别

打开httpfox抓包插件,在百度中搜索126.com,搜索项中点击网站入口,通过抓包工具,查看http请求

Remote Address是什么 remote-addr_IP

 

在http请求的Headers部分可见Referer:


什么是Referer?

Referer是HTTP请求头信息里面的一个常见字段,它提供了访问来源的信息。比如说,在某网站上看到一个广告并点了进去,那么我就可能会在HTTP 协议在请求(request)的头信息里面看到Referer这一字段给出“引荐网页”的URL,让人知道我们是通过“某网站”进入的这个广告页。

Referer存在的条件

在浏览器向服务器请求资源的时候,Referer字段并不是总是存在的,其中在以下几种情况中会发送Referer字段

1.点击网页上的连接

2.提交表单(post或get)

3.网页加载静态资源,比如加载图片、脚本、样式(js的script标签及html中img标签的src属性)。

以下是几种获取不到Referer的几种情况

1.直接在浏览器内输入URL

2.通过浏览器内的书签或收藏

3.第三方软件内容中的连接(如Word,Excel等)

4.从HTTPS的网站跳转到HTTP的网站或SSL认证网站跳入

那么Referer有什么作用呢?

1.通过Referer我们可以知道访问来源的信息,所以可以用来做一些网站流量的来路统计

2.防止图片盗链,当我们不希望自家网站图片外链被其他网站加载时,就可以基于Referer字段来实现,可以通过判断该字段是否是自家URL或者是属于白名单里的URL如果是则放行允许加载。

3.在某些情境下我们不希望暴露当前的原始网址,比如一个社交网站会有每个用户的简介页面,用户会在他们的个人主页中添加一些链接。社交网站可能不希望泄露用户的个人主页URL给被链接的网站(因为个人主页URL可能会泄露其主人的身份信息)。
一些社交网站可能想通知其他网站该链接是从社交网站发起的,但不想泄露包含用户信息的链接(例如,微博中的链接希望告诉对方该链接是在微博中连接过来的,但不希望告诉对方从谁的微博连接过来)。

referrer策略

Referrer策略包含以下值:

  1. 空字符串:空字符串表示没有referrer策略,默认为no-referrer-when-downgrade。
  2. no-referrer:最简单的策略是“no-referrer”,表示所有的请求都不带referrer。
  3. no-referrer-when-downgrade:主要针对于受TLS保护的URL(如https),简单的说就是https的页面中,当连接的资源也是https的,则发送完整的referrer,如果连接的资源是http的,就不发送referrer
  4. same-origin:对于同源的链接,会发送referrer,其他的不会。
  5. origin:这个策略对于任何资源来说只发送源的信息,不发送完整的url。
  6. strict-origin:这个策略类似于origin和no-referrer-when-downgrade的合体,如果一个https页面中链接到http的页面或资源,则不会发送referrer。http页面链接以及https链接到https都只发送来源页面的源信息。
  7. origin-when-cross-origin:该策略在同源的链接中发送完整的URL,其他情况仅发送源信息。相同的域名,http和https协议被认为是非同源的。
  8. strict-origin-when-cross-origin:对于同源请求,发送完整的URL;对于同为https的,只发送源信息;对于http页面只发送源信息;https页面中的http请求不发送referrer。
  9. unsafe-url:这个主要是解决https页面中的http资源不发referrer的问题,它会使在https页面中http资源发送完整的referrer。

Referrer策略可以通过以下方法声明:
1.通过http请求头中的Referrer-Policy字段,例:Referrer-Policy: no-referrer
2.通过meta标签,name为referrer,例:<meta name=”referrer” content=”no-referrer” />
3.通过<a>、<area>、<img>、<iframe>、<link>元素的referrerpolicy属性。例:<a href=“http://example.com” referrerpolicy=“origin”>
4.通过<a>、<area><link>元素的rel=noreferrer属性,例:<a href='http://example.com' rel="noreferrer" target="_blank">test</a>
5.通过隐式继承

注意事项
Referrer策略还有其他历史遗留的值:
1.never等价于no-referrer
2.default等价于no-referrer-when-downgrade
3.always等价于unsafe-url
4.不建议使用上面三个值,建议使用后面的新值

兼容性
IE:不支持(IE高版本中隐式支持default,https页面拉取的http资源不会加referrer)
Edge:仅支持较早版本的值(never、always、origin、default)
Firefox:36+
Chrome:21+
Safari:7.1+(仅支持较早版本的4个值)
Opera:15+
iOS Safari:8+(仅支持较早版本的4个值)

注意事项

  • 当然,对于某些恶意用户,也可能伪造Referer来获得某些权限,在计网站时要考虑到这个问题。
  • 还可用做电子商务网站的安全,在提交信用卡等重要信息的页面用referer来判断上一页是不是自己的网站,如果不是,可能是黑客用自己写的一个表单,来提交,为了能跳过你上一页里的javascript的验证等目的。
  • 但是注意不要把Rerferer用在身份验证或者其他非常重要的检查上,因为Rerferer非常容易在客户端被改变。 (火狐的一个插件RefControl修改Referer引用)

对浏览器来说,一般以下几种情况是不会发送Referer,因为可能有潜在的安全问题:

1.用户手动输入网址或是从收藏夹/书签中访问。
2.页面从Https跳转到Http;应该是处于安全考虑,该点在RFC-2616中有说明;主流浏览器均遵守这个规则,比如IE、FF;但默认情况下Https到Https是会发送Referer的。安全问题在于有可能把地址中含有的一些关于用户的敏感信息发送到其他恶意网站上。
3、由于FF提供了很强大的自定义参数设置功能,所以我们可以通过about:config页面修改以下两个选项的默认设置:
network.http.sendRefererHeader (default=2)
设置Referer的发送方式,0为完全不发送,1为只在点击链接时发送,在访问页面中的图像什么的时候不发送,2为始终发送。

1. getRemoteAddr()是获得客户端的ip地址    
2. getRemoteHost()是获得客户端的主机名
3. 获取客户端的IP地址的方法是:request.getRemoteAddr(),这种方法在大部分情况下都是有效的。但是在通过了Apache,Squid等反向代理软件就不能获取到客户端的真实IP地址了(比如负载均衡处理)。
4. public String getRemortIP(HttpServletRequest request) {  
5.   return request.getRemoteAddr();  
6. //return request.getRemoteHost();
7.   }   
8. return request.getHeader("x-forwarded-for");
9.  }
  1. 此处,获取ip,当x-forwarded-for为null时,表示请求没有经过处理,此时调用getRemoteAddr()和getRemoteHost()都可获取真实ip
  1. 反之,则getHeader("x-forwarded-for")为真实的ip。