关于HRBP工具箱“六个盒子”的介绍和应用

一、六个盒子的简洁介绍
Q、六个盒子是什么鬼?

六个盒子,江湖人称”六个BOX”,也叫韦斯伯德的六盒模型,是组织内部视角、不断检视业务实现过程的利器。

六个维度分别是“使命/目标、结构/组织、关系/流程、奖励/激励、支持/工具、管理/领导”。

Q、六个盒子核心作用是什么?

用HR小伙伴的话来说“不管业务和组织架构怎么变,六个盒子跑一遍。”

因此,六个盒子也是一种简单而实用的组织诊断工具,可以帮助我们“盘点现状”、“打开未来”,以及搭建起现实与未来的桥梁。帮助建立业务团队的组织大图起到了全面了解自已的关键作用。

Q、六个盒子具体怎么用?

可以作为几种工具:

1、盘点工具:能够就组织现状进行盘点。

2、诊断工具:建立全面的组织视角,从解决单个问题到更全面看组织,

3、沟通工具:是一套简洁的语言,就组织状态开启有效的沟通。

4、平台工具:HR和业务在一张大图上工作,能有效开展工作。

Q、六个盒子使用场景是什么?

有三个主要场景:

1、新团队摸底:当你进入一个新团队,想全面了解这个团队。

2、组织中调频:当你和关键人对话,深度进行组织盘点和现实状况讨论。

3、组织架构调整前:可用于帮助梳理现状,找到调整后的目标。

Q、六个盒子适用于那些人?

业务Leader,期望对于组织有所发展的管理者,有责任感的领导者,和业务伙伴战斗在一起的HRBP们。

Q、HR怎么才能玩转六个盒子?

两个要点:

1、日常对话:平时跟业务 leader或核心骨干一对一的对话,有一个思维模型。

2、专业讨论:与业务核心骨干团队,同业务或管理大图上讨论,有一个专业工具。

Q、六个盒子到底讲的是什么?

核心内容是:

1.使命——组织是否有清晰的使命?员工是否理解并认同公司的使命?

2.结构——企业内部工作是如何被分配的?考虑到使命时,人力资源的分配是否合理?

3.奖励——考察所有需要完成的任务是否都有相对应的激励措施。奖励是支持还是阻碍了任务的达成?

4.关系——公司各单元协调的方式是怎么样的?缺乏协调是否会引起矛盾?

5.支持——支持组织工作的系统和流程是怎么样的?

6.管理——被视作密切观察其他五个盒子中非正常事件或意外结果的最后一个盒子。其作用就是否确保其他五个盒子能处于均衡的状态?若失衡时要采取怎么样的行动及时修正?

Q、六个盒子落地关键点是什么?

三个关键点:

1、Who和谁用?关键是不是一个人去面对这些盒子,而是和谁一起来探寻,即“INPUT”高质量的信息将对于”OUTPUT”的有效性是重要影响。

2、Why为什么用?考虑到六个盒子的作用边界,六个盒子是站在较为微观看组织大图。就像雷达屏幕一般,显现在上面的光点告诉了我们组织的事情——也就是业务实现过程的状况。

3、How怎么用?有团队DIY出了属于自己的问题和内容,也玩出了独特的味道,通过在一起共识和讨论提升了效率。不但听懂了别人的还清晰地表达了自己所想的内容。

Q、六个盒子应用有什么要注意的?

常见的一些误区:

1、过度聚焦:把所有问题都归结为某个盒子,并从这个盒子切进去找解决方案。

2、过度简化:只关注某个盒子的静态状况,忽略六个盒子动态的演进过程

3、过度排序:把六个盒子当作强制排序,忽略了他们同时存在,彼此影响。

4、过度理想:把六个盒子当成万能钥匙,不考虑六个盒子以外的其他因素。

二、六个盒子的案例解读
案例:六个盒子在阿里云的案例

第一盒子:使命、战略、目标

使命——愿景

战略——三到五年的蓝图、里程碑

目标——一年左右切实可行的衡量指标

1、战略大图

  • 是否有清晰的客户价值
  • 是否清晰
  • 是否让人兴奋
  • 大家是否有Buy in

2、战略大图落地

  • 战略路径是否清晰
  • 核心抓手是什么 ——能够最体现战略的落地点核心点或是核心事件或活动,例如 阿里云200个HC,搜索:移动搜索、云搜索
  • 是否被合理分解为各个部门的目标:化学分解而非物理分解

3、如何衡量(KPI是什么)

4、拼大图

  • 这个环节主要是了解业务目标,业务逻辑,暂时不需要考虑人的因素。
  • 抓手——从量变到质变
  • 关于客户价值——

(1)客户是谁,细分客户市场

(2)客户需要解决的问题是什么

(3)什么情况出现才表示的问题解决了(解决问题的展现结果)

例如:运维的客户需求:系统的稳定/系统的灵活/成本

(4)怎么判断客户价值已经清楚了?——看业务部门是否已近达成一致,客户的声音是被清晰的在内部传递的。阿里云是技术驱动业务。

第二个盒子:结构&组织

1、排兵布阵

  • 分工&职责是否都清晰
  • 模糊边界越来越多,挑战在于:各司其职、混战
  • 如何看待跨界

2、核心关键部门Leader是否胜任?

  • 如何搭配
  • 有什么好处和风险
  • 能力有啥缺失?有没有发展计划?

3、战略所需要的核心能力是否具备?

4、扁平化是趋势、弹性、自我驱动

  • 清晰的分析组织,从业务和人来看,哪些人是外招、哪些是内招、培养的周期。
  • 阿里云之前的业务需要,主要招聘技术人员,但目前技术稳固的情况下,人员可能要更多的进行内部流转。通过人才盘点的形式,了解组织的需求。
  • 呼之即来,来之即战,战之即散——未来的方向。

第三个盒子:关系&流程

1、部门和主体其他部门的关系

  • 与集团相关业务块的关系
  • 产品、工程、技术的关系
  • 和客户部门、横向部门的关系
  • 创新小组和本职业务的关系

2、关注官方的流程,更要关注民间的流程

3、边界模糊是常态

4、组织文化:让大家在面临选择时判断一致,而不依赖于流程制度,人际依赖越来越强。

第四个盒子:酬劳&激励

1、奖励:由外而内 激励:由内而外

  • 什么是个体动力
  • 是否了解和释放了个体的能量和潜力
  • 对什么行为和结果予以奖励
  • 对什么行为和结果给予鼓励
  • 谁被奖励
  • 谁奖励个体
  • 奖励是公平和有序的吗?
  • 奖励个体还是团队

2、如何奖励和激励

  • 物质、奖项、无意识的潜意识影响、办公环境,鼓励创新
  • 多长时间做一次激励,激励的范围(集体还是个人)

第五个盒子:支持&工具

1、是否有足够的资源去做?

  • 硬的:技术资源
  • 软的:关系、连接

2、公开透明的协调程序

计划、预算、控制、信息、评估的流程

3、支持:

  • 技术、服务、合作、制度、工具
  • 禁区和底线清楚
  • 协调自己控制区域以外资源的能力
  • 员工有不断成长能力的机会和资源
  • 关系:你对合作伙伴的了解认同和合作伙伴对你的了解认同。

第六个盒子:管理和领导

正确的做事&做正确的事

1、在第一个盒子的表现:指明方向,使众人行

2、在第二个盒子的表现:排兵布阵,知人善用

3、在第三个盒子的表现:建机制,造土壤

4、在第四个盒子的表现:梦想驱动,奖谁罚谁

5、在第五个盒子的表现:协调资源,扩大影响力

三、六个盒子在阿里的实践心得
分享嘉宾:2011年加入阿里电销的前线团队,2014年的四月份转的后台HRG 。

【提问:你在什么样的背景下选用了“六个盒子”工具?】

阿里HRG:

1、业务的差异:我转到后台,发现前线与后台的业务和组织非常不同:业务上,前台是相对简单一个业务线,而后台相对复杂,有18条业务线,包括商业产品、策划、品控、培训、资源管理、业务拓展等;同时,我的搭档在后台也变成了五个。

2、遇到的挑战:随着这些业务现状的差异性,组织的方式也变得非常有挑战。前线很统一,后台变得更加要灵活,作为HRG,我变得很难有精力全程参与所有的大小业务会议,最大挑战是如何快速看清业务和组织的现状,找到HR的核心发力点。

3、使用的初衷:六个盒子的工具给了我一些快速地做出诊断的方法,与业务伙伴们一起找到核心的发力点,这是我运用这个工具的初衷。

【提问:在运用六个盒子的过程中,需要做什么样的准备吗?】

阿里HRG:六个盒子在阿里就那么几个人会讲课,我也听过好几个版本的内容。我自己面临的是一个小的组织,想要请到讲师来帮助,现实上比较困难的,最后决定我自己来,也就从这儿开始了我与六个盒子的缘份。前面主要有几个准备动作:

第一是明确了只有我自己来授课和帮助团队一起跑六个盒子。

第二是如果我来做,用什么方式和流程来做。

第三是从理论和实践活动都要做什么准备。

【提问:六个盒子落地的现场什么样的?】

阿里HRG:分为理论和实践两部分:

1、理论的部分:比如开场部分梳理部分,要讲清楚几个问题:“六个盒子是什么?”、“为什么要用六个盒子?”、“六个盒子的价值是什么?”、“六个盒子能够带来什么?”。比如说“使命和目标”这个盒子,核心讲清楚几个问题,“今天为什么我们要谈这个内容?”、“使命和目标分别是什么?”、“为什么当下我们要重点谈目标”、“这个内容需要回答清楚的内容是什么?”、“如何理解客户、合作伙伴、支持方的差异”、“内容如何衡量好与不好的标准是什么?”等等。

2、实战的部分:关键是是现场前的准备,要在流程、问题、机制,如何做引导等上进行设计和思考,当然核心的目的是什么呢?组织诊断的核心目的是什么?我认为应该是真实地呈现现状和问题,而不是抱怨过去,或是探讨未来的愿景。所有的人和流程的设计,最终的目的是让所有人都参与进来,敢于讲真话,是整个过程的核心要Hold的场域。

【提问:第一次使用六个盒子,大家给予你的反馈如何?】

阿里HRG:透过六个盒子跑几遍后,我发现的变化是:

1、业务Leader的反馈:从原来的焦虑变得有力量而笃定。

2、业务同学的反馈:从原来认为是Leader的事情变成是我们共同要面对的事情。

3、我个人的反馈:从原来我个体的看见,只是点对点的建议,力量是不足的,现在感觉更有力量和动力。

4、整体上的反馈:经过六个盒子后,大家共同看见,有一个共同的语境,在同一个视角去发现业务和组织的现状,变得不再是自己,而是和一群人一起,更容易达成共识,这是大家共同推动的结果。

【提问:”六个盒子”实践最困难是什么?】

阿里HRG:还没有开始的时候是有很多的焦虑和担心的,担心搞砸了,担心搭档不满意,担心现场自己HOLD不住。可一旦开始了,就没有什么困难可言了,在现场就会很自然的在场子里,和搭档一起坚定地往下走,边做边设计。

我内心的信心是:不是一个人的事,是大家共同在做组织诊断。

【提问:如果要告诉即将使用”六个盒子”的小伙伴们一句话,你会说什么?】

阿里HRG:几场跑下来我最大的感受是,想要做一件事很难,特别是在策划的阶段,但当真正的实现时,发现没有想象中这么难,只要开始实践开始时,就成功90%成了。

实践的勇气非常重要,就如奥托说到0.8的原则,就是边实践边反思。我们要相信六个盒子的力量,它足够帮助到业务和组织的,就是实践实践再实践,实践的力量可以帮助我们活出来。

来源 http://www.hr.com.cn/p/1423415644

NGINX多层转发或使用CDN之后如何获取用户真实IP

##1.背景知识

1.1. 前提知识点:

关键词:ngx_http_realip_moduleHAProxy反向代理出口IP

还有nginx中的几个变量:

  • remote_addr

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

  • X-Forwarded-For(简称XFF)

    X-Forwarded-For 是一个 HTTP 扩展头部。HTTP/1.1(RFC 2616)协议并没有对它的定义,它最开始是由 Squid 这个缓存代理软件引入,用来表示 HTTP 请求端真实 IP。如今它已经成为事实上的标准,被各大 HTTP 代理、负载均衡等转发服务广泛使用,并被写入 RFC 7239(Forwarded HTTP Extension)标准之中。

    XFF的格式为:

    X-Forwarded-For: client, proxy1, proxy2
    

    XFF 的内容由「英文逗号 + 空格」隔开的多个部分组成,最开始的是离服务端最远的设备 IP,然后是每一级代理设备的 IP。(注意:如果未经严格处理,可以被伪造

    如果一个 HTTP 请求到达服务器之前,经过了三个代理 Proxy1、Proxy2、Proxy3,IP 分别为 IP1、IP2、IP3,用户真实 IP 为 IP0,那么按照 XFF 标准,服务端最终会收到以下信息:

    X-Forwarded-For: IP0, IP1, IP2
    

    Proxy3 直连服务器,它会给 XFF 追加 IP2,表示它是在帮 Proxy2 转发请求。列表中并没有 IP3,IP3 可以在服务端通过 Remote Address 字段获得。我们知道 HTTP 连接基于 TCP 连接,HTTP 协议中没有 IP 的概念,Remote Address 来自 TCP 连接,表示与服务端建立 TCP 连接的设备 IP,在这个例子里就是 IP3。Remote Address 无法伪造,因为建立 TCP 连接需要三次握手,如果伪造了源 IP,无法建立 TCP 连接,更不会有后面的 HTTP 请求。但是在正常情况下,web服务器获取Remote Address只会获取到上一级的IP,本例里则是proxy3 的 IP3,这里先埋个伏笔

  • X-Real-IP

    这又是一个自定义头部字段,通常被 HTTP 代理用来表示与它产生 TCP 连接的设备 IP,这个设备可能是其他代理,也可能是真正的请求端,这个要看经过代理的层级次数或是是否始终将真实IP一路传下来。(注意:如果未经严格处理,可以被伪造

1.2.前提与铁律

铁律:当多层代理或使用CDN时,如果代理服务器不把用户的真实IP传递下去,那么业务服务器将永远不可能获取到用户的真实IP。

1.3.用户真实IP的来源和现实情况

首先说用户真实的IP也会存在很多人共用一个IP的情况。用户的请求到达业务服务器会经过以下几种情形:

####1.3.1.宽带供应商提供独立IP

比如家里电信宽带上网,电信给分配了公网ip,那么一个请求经过的ip路径如下:

192.168.0.101(用户电脑ip)–>192.168.0.1/116.1.2.3(路由器的局域网ip及路由器得到的电信公网ip)–>119.147.19.234(业务的前端负载均衡服务器)–>192.168.126.127(业务处理服务器)。

这种情况下,119.147.19.234会把得到的116.1.2.3附加到头信息中传给192.168.126.127,因此这种情况下,我们取得的用户ip则为:116.1.2.3。如果119.147.19.234没有把116.1.2.3附加到头信息中传给业务服务器,业务服务器就只能取上上一级的119.147.19.234的内网IP.

1.3.2.宽带供应商不能提供独立IP

宽带提供商没有足够的公网ip,分配的是个内网ip,比如长宽等小的isp。请求路径则可能为:

192.168.0.123(用户电脑ip)–>192.168.0.1/10.0.1.2(路由器的局域网ip及路由器得到的运营商内网ip)–>211.162.78.1(网络运营商长城宽带的公网ip)–>119.147.19.234(业务的前端负载均衡服务器)–>192.168.126.127(业务处理服务器)。
这种情况下得到的用户ip,就是211.162.78.1。 这种情况下,就可能出现一个ip对应有数十上百个用户的情况了(受运营商提供的代理规模决定,比如可能同时有几千或上万的宽带用户都是从211.162.78.1这个ip对外请求)。

####1.3.3.手机2g上网
网络提供商没法直接提供ip给单个用户终端,以中国移动cmwap上网为例,因此请求路径可能为:

手机(手机上没法查看到ip)–> 10.0.0.172(cmwap代理服务器ip)–>10.0.1.2(移动运营商内网ip)–>202.96.75.1(移动运营商的公网ip)–>119.147.19.234(业务的前端负载均衡服务器)–>192.168.126.127(业务处理服务器)。
这种情况下得到的用户ip,就是202.96.75.1。2008年的时候整个广东联通就三个手机上网的公网ip,因此这种情况下,同一ip出现数十万用户也是正常的。

####1.3.4.大厂,有几万或数十万员工,但是出口上网ip就一个
这种也会出现来自同一ip的超多用户,比如腾讯、百度等某一个办公区,可能达到几万人,但出口IP可能就那么几个。

2.如何获取用户真实IP

2.1. 当业务服务器直接暴露在公网上,并且未使用CDN和反向代理服务器时:

可以直接使用remote_addr。如 PHP 可以直接使用

$_SERVER['REMOTE_ADDR']

这时候,HTTP_X_FORWARDED_FOR 和 HTTP_X_REAL_IP 都是可以被伪造的,但REMOTE_ADDR是客户端和服务器的握手IP,即client的出口IP,伪造不了。
比如用下面这条命令来请求一个php文件,并且输出$_SERVER信息

curl http://10.200.21.32/test.php -H 'X-Forwarded-For: unkonw, <alert>aa,11.22.33.44,11</alert>" 1.1.1.1' -H 'X-Real-IP: 2.2.2.2, <a>'

结果是(只取部分信息,10.100.11.25是我电脑的IP,服务器是内网服务器,所以不会有公网IP)

[HTTP_X_FORWARDED_FOR] => unkonw, <alert>aa,11.22.33.44,11</alert>" 1.1.1.1
[REMOTE_ADDR] => 10.100.11.25
[HTTP_X_REAL_IP] => 2.2.2.2, <a>

可以看到,HTTP_X_FORWARDED_FOR 和 HTTP_X_REAL_IP 是万万不可直接拿来用的。使用$remote_addr是明智的选择。

比如我们伪造一下来源IP发给著名的 ip138.com

curl http://1212.ip138.com/ic.asp -H 'X-Forwarded-For: unkonw, <alert>aa,11.22.33.44,11</alert>" 1.1.1.1'

它原样输出了我们伪造的XFF。

2.2.在代理服务器或CDN之后的业务服务器

前提:上面的每一层代理或CDN,都将原始请求的 remote_addr 一路传递下去。我们先来看其中一种方案。

如果web服务器上层也是使用nginx做代理或负载均衡,则需要在代理层的nginx配置中明确XFF参数,累加传递上一个请求方的IP到header请求中。以下是代理层的nginx配置参数。

    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_set_header X-NginX-Proxy true;

如果web服务器前面使用了HAProxy,则需要增加以下配置来将用户的真实IP转发到web服务器。

    option forwardfor

如果想在业务服务器获取完整的链路信息,还是通过XFF获取,需要在nginx的配置中加一条配置,加上此配置可以让我们获取整个链路信息:

fastcgi_param  HTTP_X_FORWARDED_FOR $http_x_forwarded_for;

实测此参数最好加在被 include 的fastcgi.conf中,就是有一堆fastcgi_param配置的那个文件否则就写入location段。这个配置可能会影响你的nginx日志,这个后续会详细说明。如果不配置此项,则我们在WEB SERVER 上直接获取到的XFF信息则是上一个代理层的IP。当然,也不影响获取用户真实IP。不过如果你是在调试配置的情况下,就不方便查看整个链路了。

2.2.1 在只有一层代理的情况下

我们按上面的配置发起一个伪造请求, 10.100.11.25 是我电脑的IP,链路为:

10.100.11.25(client)->10.200.21.33(Proxy)->10.200.21.32(Web Server)

curl 请求:

curl http://10.200.21.33:88/test.php -H 'X-Forwarded-For: unkonw, <8.8.8.8> 1.1.1.1' -H 'X-Real-IP: 2.2.2.2'

结果如下:

[HTTP_X_FORWARDED_FOR] => unkonw, <8.8.8.8> 1.1.1.1, 10.100.11.25
[REMOTE_ADDR] => 10.200.21.33
[HTTP_X_REAL_IP] => 10.100.11.25

我们可以看到,XFF被附加上了我的IP,但前面的一系列伪造内容,可以轻易骗过很多规则,而HTTP_X_REAL_IP 则传递了我电脑的IP。因为在上面的配置中,X-Real-IP 已经被设置为握手 IP。 但多层代理之后,以上面的规则,显然 HTTP_X_REAL_IP 也不会是真实的用户IP了。而 HTTP_X_FORWARDED_FOR 则在原有信息(我们伪造的信息)之后附上了握手 IP 一起传递过来了。

2.2.2 在两层或更多代理的情况下

我们这里只测试两层,实际链路为:

10.100.11.25(client)->10.200.21.34(Proxy)->10.200.21.33(Proxy)->10.200.21.32(Web Server)

Curl 命令:

curl http://10.200.21.34:88/test.php -H 'X-Forwarded-For: unkonw, <8.8.8.8> 1.1.1.1' -H 'X-Real-IP: 2.2.2.2'

两层代理的情况下结果为:

[HTTP_X_FORWARDED_FOR] => unkonw, <8.8.8.8> 1.1.1.1, 10.100.11.25, 10.200.21.34
[REMOTE_ADDR] => 10.200.21.33
[HTTP_X_REAL_IP] => 10.200.21.34

根据上面的情况,怎么挑出真正的用户IP呢?设想三种方案:
* 第一层代理将用户的真实 IP 放在 X-Real-IP 中传递下去,后面的每一层都使用 X-Real-IP 继续往下传递。配置为:

proxy_set_header X-Real-IP $remote_addr;    # 针对首层代理,拿到真实IP
proxy_set_header X-Real-IP $http_x_real_ip; # 针对非首层代理,一直传下去
  • 从首层开始,将用户的真实IP 放在 X-Forwarded-For 中,而不是累加各层服务器的 IP,但这样也不够合理,因为丢掉了整个链路信息。配置为:
proxy_set_header X-Forwarded-For $remote_addr; # 针对首层代理
proxy_set_header X-Forwarded-For $http_x_forwarded_for; # 针对非首层代理
  • 从 X-Forwarded-For 中获取的用户真实IP,排除掉所有代理IP,取最后一个符合IP规则的,注意不是第一个,因为第一个可能是被伪造的(除非首层代理使用了握手会话 IP 做为值向下传递)。

一般CDN都会将用户的真实 IP 在XFF中传递下去。我们可以做几个简单的测试就能知道我们该怎么做。

注意:nginx配置的这两个变量:
* $proxy_add_x_forwarded_for 会累加代理层的IP向后传递
* $http_x_forwarded_for 仅仅是上层传过来的值

3.配合nginx realip模块获取用户真实IP

我们应该秉承一个原则:

能通过配置让事情变的更简单和通用的事儿,就不要用程序去解决。即环境对程序透明。这当然少不了系统运维人员的辛苦。

如果能在配置中理清,就不必用复杂的程序去解决,因为Server上可能有各种应用都要来获取用户IP,如果规则不统一,结果会不一致。
程序不知道链路到底经过了几层才转到web server上,所以让程序去做兼容并不是个好主意。索性就让程序把所有的代理都当成透明的好了。

终于说到重点了。上面介绍的三种方法中,如果不能保证前面的代理层使用我们指定的规则,这时候怎么办呢?只能使用第三种方法。然后我们将各层代理的IP排除在外,就取到了真实的用户IP。这个可以使用nginx的一个模块儿 realip_module 来实现。原理是从XFF中抛弃指定的代理层 IP,那么最后一个符合规则的就是用户 IP。也可以配合第一起方法一起使用。但无论如何,首层代理的规则最重要,直接影响后面的代理层和web service的接收结果。

nginx realip_module 模块需要在编译nginx的时候加上参数--with-http_realip_module

然后在nginx配置中增加以下配置(可以在http,server或location段中增加)

    # set user real ip to remote addr
    set_real_ip_from   10.200.21.0/24;
    set_real_ip_from   10.100.23.0/24;
    real_ip_header     X-Forwarded-For;
    real_ip_recursive on;

set_real_ip_from 后面是可信 IP 规则,可以有多条。如果启用CDN,知道CDN的溯源IP,也要加进来,除排掉可信的,就是用户的真实IP,会写入 remote addr这个变量中。

比如在PHP中可以使用$_SERVER['REMOTE_ADDR'] 来获取。而WEB SERVER 不使用任何反向代理时,也是取这个值,这就达到了我们之前所说的原则。

real_ip_recursive 是递归的去除所配置中的可信IP。如果只有一层代理,也可以不写这个参数。

然后我在外网请求一下,结果是这样的

[HTTP_X_FORWARDED_FOR] => unkonw, <8.8.8.8> 1.1.1.1, 112.193.23.51, 10.200.21.50
[REMOTE_ADDR] => 112.193.23.51

112.193.23.51 是 client 的 IP, 10.200.21.50 是WEB SERVER 前面的负载均衡。 真实IP拿到了。

再说下nginx日志

如果nginx日志中记录了XFF,那么可能会有一些是我们不想记录的,比如我们现在使用的默认的nginx日志格式为:

log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

这时候由于XFF里包含太多信息,甚至可能是一些伪造的未经过滤的文本,在使用和分析日志的时候会出现麻烦,所以我们干脆不记录它。nginx 的日志格式log_format还有一个默认值“combined”. 默认格式为:

log_format combined '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent"';

我们使用这个格式就好了。

总结

我们建议使用以下规则:
* 首层代理将握手 IP 附在 X-Forwarded-For 上一直向后传递(或者将 X-Forwarded-For 设置为握手 IP 向后传递),后面的每一层累加握手 IP 往后传递。
* 首层代理将握手 IP 设置为 HTTP 请求头的 X-Real-IP 中向后传递。后面的每一层原样传递下去(有则原样传递,无则设置为握手 IP )。

握手IP:即请求方的 remote_addr, 重要的话要说三遍:

运维很重要,首个代理层的处理方式很重要。

运维很重要,首个代理层的处理方式很重要。

运维很重要,首个代理层的处理方式很重要。

在只有运维最清楚网络环境的时候,尽量通过配置对应用透明。减少应用层的复杂判断。如果环境很复杂,比如使用了CDN,则有可能需要多方协调。

参考资料

  • http://nginx.org/en/docs/http/ngx_http_realip_module.html
  • https://www.zhihu.com/question/23810075
  • http://www.cnblogs.com/zhengyun_ustc/archive/2012/09/19/getremoteaddr.html
  • http://zhensheng.im/2013/08/31/1952/MIAO_LE_GE_MI
  • http://stackoverflow.com/questions/25929599
  • http://www.ttlsa.com/nginx/nginx-get-user-real-ip/
  • https://imququ.com/post/x-forwarded-for-header-in-http.html
  • http://freeloda.blog.51cto.com/2033581/1288553
  • http://nginx.org/en/docs/http/ngx_http_log_module.html

composer安装包时报any version for your minimum-stability (stable)

今天要装一个包endroid/qrcode,可是死活装不上。
composer require 安装的时候一直报

[InvalidArgumentException]
Could not find package endroid/qrcode at any version for your minimum-stability (stable). Check the pac
kage spelling or your minimum-stability

然后新composer init一个空项目就能装上了。然后开始分析我的现有项目的composer.json文件。

最后发现是之前使用了国内的composer镜像,加上了一个配置


{
"repositories": [
{
"packagist": false
},
{
"type": "composer",
"url": "http://packagist.phpcomposer.com"
}
]
}

之前只把后一段给删了,没有删前一段。把前一段的“packagist”: false 也删了就好了。

禁用chrome firefox 的 WebRTC功能防止真实IP泄漏

无论是使用VPN还是其它代理方式,很多时候我们不希望暴露自己的真实IP,且一直以来我们认为VPN是安全的,所有流量都会走VPN。
但最近暴露出一个WebRTC特性,会暴露我们的真实IP。适用浏览器:chrome,firefox. safari则没有问题。

只需要一段js代码就可以获取我们的真实IP。一旦被想时时监控别人的人知道并使用此方法钓鱼,便可直接获得原始IP。这简直太恐怖了。

看来以后如果重装系统,第一件事儿把cnnic证书删除后,第二件事儿就是禁用WebRTC.

有一个插件可以方便的禁用WebRTC, 插件下载

Firefox可在浏览器上输入:about:config。之后搜索:media.peerconnection.enabled。找到它后双击,将其改成 false 即可。

原理可以参考一下这里:https://github.com/diafygi/webrtc-ips

或者直接把下面这段代码放在chrome的console里运行一下,就知道自己的真实IP了。


//get the IP addresses associated with an account
function getIPs(callback){
var ip_dups = {};

//compatibility for firefox and chrome
var RTCPeerConnection = window.RTCPeerConnection
|| window.mozRTCPeerConnection
|| window.webkitRTCPeerConnection;
var mediaConstraints = {
optional: [{RtpDataChannels: true}]
};

//firefox already has a default stun server in about:config
// media.peerconnection.default_iceservers =
// [{"url": "stun:stun.services.mozilla.com"}]
var servers = undefined;

//add same stun server for chrome
if(window.webkitRTCPeerConnection)
servers = {iceServers: [{urls: "stun:stun.services.mozilla.com"}]};

//construct a new RTCPeerConnection
var pc = new RTCPeerConnection(servers, mediaConstraints);

//listen for candidate events
pc.onicecandidate = function(ice){

//skip non-candidate events
if(ice.candidate){

//match just the IP address
var ip_regex = /([0-9]{1,3}(\.[0-9]{1,3}){3})/
var ip_addr = ip_regex.exec(ice.candidate.candidate)[1];

//remove duplicates
if(ip_dups[ip_addr] === undefined)
callback(ip_addr);

ip_dups[ip_addr] = true;
}
};

//create a bogus data channel
pc.createDataChannel("");

//create an offer sdp
pc.createOffer(function(result){

//trigger the stun server request
pc.setLocalDescription(result, function(){}, function(){});

}, function(){});
}

//Test: Print the IP addresses into the console
getIPs(function(ip){console.log(ip);});

无聊的长城

我TM一个技术博客你没事儿老封我干吗啊。防民之口到这个地步。。。
如果你是一个东德边防士兵,发现有人逃往西德,你可能因职责必须要开枪,但如果你有良心,那就将枪口抬高一寸。

python学习笔记之字符串

字符串格式化:
format = “hello %s, %s enough for ya?”
values = (‘world’,’hot’)
print format % values
结果:hello world, hot enough for ya?
注:如果不是在命令行执行,把print后面的用括号括起来

与php类似但函数或方法名不一样的地方:
php explode=> python split
php trim => python strip
php implode => python join

python学习笔记之字典

字典是最与php的数组相似的序列结构。python的列表只能是以索引数字开头并且顺序递增的序列。字典则可以是以字母为key的序列。

元组一般用圆括号来表示,如(1,2,3)
列表一般用方括号来表示,如[1,2,3]
而字典(dict)则用大括号来表示,如{‘a’:1,’b’:2,’c’:3}
与php不同,php的key,value是用key=>value来表示,python而是用冒号“:”来分隔。

字典可以使用大括号书写键和值或使用dict函数来创建。
dict函数可以指定两个元组或列表来对应创建字典。如:
items = [(‘name’,’gumby’),(‘age’,’42’)]
d = dict(items)
与列表的区别:
k in d(d为字典),查找的是键,而非value,表达式 v in l(l为列表)则用来查找值,而不是索引。

一些字典方法:

clear 清除字典。“原地操作”,不会返回值,类似于列表的sort方法
copy方法返回一个相同键值对儿的新字典。浅复制。因为值本身就是相同的,而不是副本。
在副本中替换值的时候,原始字典不受影响。但是如果修改了某个值,原始字典也会改变。避免这个问题的一种方法就是使用深度复制(deep copy)。
from copy import deepcopy
deepcopy(d)

d.fromkes方法使用给定的值创建新字典。每个键默认对应的是None
get方法获取一个不存在的键时,不会报错,会返回None
has_key方法相法于表达式k in d
items方法将所有的字母项以列表的方式返回。这些列表中的每一项都来自于(键,值)。但是项在返回时没有特殊的顺序。
iteritmes的方法大致相同,但是会返回一个迭代器对象而不是列表。在很多情况下iteritems效率更高。迭代器,相当于decode json之后的对象,而非数组、
keys方法将字典中的键以列表的形式返回。而iterkeys则返回针对键的迭代器。
pop弹出给定键的值。popitem会弹出最后一个元素(实际上是随机项),但如果想一个接一个地移除并处理字典,popitem效率就会高很多,因为不必先获取字典的键值列表。

values和itervalues方法以列表或迭代器的形式返回字典中的值,返回值的列表可以包含重复项。

python学习笔记之列表和元组

列表和元组的主要区别在于,列表可以修改,而元组不可以修改。也就是说,如果要根据要求来添加元素,那么列表可能会更好用。序列不能修改的时候,使用元组更合适。

序列

索引:索引就是php数组的键值。从0开始。
分片:php的slice,如果number[3:9] 注意,截取的值是从第4个索引开始。
步长:分片的每次操作的步进长度。不可以是0,要以是负数,即从后往前数。

序列相加:使用+号进行序列的连接操作
两种相同类型的序列才能相加。
用一个数字x乘以一个序列会生成一个新的序列,原来的序列会被重复x次。

None是一个python的内建值,它的确切含义是“什么也没有”,有的语言用null。
成员资格:使用in可以检查一个字符串是否存在一个列表或字符串中。
len可以检查列表或字符串的长度。php使用str_len和count来检查。

list函数,根据字符串来创建列表。如list(‘hello’)
注意:不能为列表一个位置不存在的元素进行赋值

append方法用于在列表末尾追加新的对象。
count方法统计某个元素在列表中出现的次数,如x.count(1)
extend方法可以在列表的末尾追加另一个序列中的多个值。
index方法从列表中找出某个值第一个匹配项的索引位置。
insert方法用于将对象插入到列表中。
pop方法从列表中移除数据,默认是最后一个。
remove方法用于移除列表中某个值的第一个匹配项。pop是操作key,remove是操作value
reverse将列表中的元素反向存放。
sort方法排序列表中的元素。
sort方法排序后并不会返回列表。所以要得到一个排序的列表要先将x赋值给y,再对y排序。
sort高级排序提供两个参数,第一个参数是必须指定一个函数在排序过程中使用,第二个是正序倒序的bool值

元组

元组与列表一样,也是一种序列。唯一不同的是元组不能修改。
元组大部分时候是通过圆括号括起来的。如(1,2,3),序列则是通过方括号括起来的。
如果要实现只包括一个值的元组,也必须加一个逗号,如(23,)
tuple函数:以一个序列做为参数并把它转换为元组。
元组的分片还是元组。就像列表的分片还是列表一样。

php生成excel列名,超过26列大于Z的方法

这是phpExcel类中的方法。今天查到了,记录一下备忘。

public static function stringFromColumnIndex($pColumnIndex = 0)
	{
		//	Using a lookup cache adds a slight memory overhead, but boosts speed
		//	caching using a static within the method is faster than a class static,
		//		though it's additional memory overhead
		static $_indexCache = array();

		if (!isset($_indexCache[$pColumnIndex])) {
			// Determine column string
			if ($pColumnIndex < 26) {
				$_indexCache[$pColumnIndex] = chr(65 + $pColumnIndex);
			} elseif ($pColumnIndex < 702) {
				$_indexCache[$pColumnIndex] = chr(64 + ($pColumnIndex / 26)) .
											  chr(65 + $pColumnIndex % 26);
			} else {
				$_indexCache[$pColumnIndex] = chr(64 + (($pColumnIndex - 26) / 676)) .
											  chr(65 + ((($pColumnIndex - 26) % 676) / 26)) .
											  chr(65 + $pColumnIndex % 26);
			}
		}
		return $_indexCache[$pColumnIndex];
	}

将列的数字序号转成字母使用:

PHPExcel_Cell::stringFromColumnIndex($i); // 从o开始

将列的字母转成数字序号使用:

PHPExcel_Cell::columnIndexFromString(‘AA’);