[转帖]HTTP X-Forwarded-For 介绍
https://www.runoob.com/w3cnote/http-x-forwarded-for.html
X-Forwarded-For 是一个 HTTP 扩展头部。HTTP/1.1(RFC 2616)协议并没有对它的定义,它最开始是由 Squid 这个缓存代理软件引入,用来表示 HTTP 请求端真实 IP。如今它已经成为事实上的标准,被各大 HTTP 代理、负载均衡等转发服务广泛使用,并被写入 RFC 7239(Forwarded HTTP Extension)标准之中。
X-Forwarded-For 请求头格式非常简单,就这样:
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 请求。不同语言获取 Remote Address 的方式不一样,例如 php 是 $_SERVER["REMOTE_ADDR"],Node.js 是 req.connection.remoteAddress,但原理都一样。
问题
有了上面的背景知识,开始说问题。我用 Node.js 写了一个最简单的 Web Server 用于测试。HTTP 协议跟语言无关,这里用 Node.js 只是为了方便演示,换成任何其他语言都可以得到相同结论。另外本文用 Nginx 也是一样的道理,如果有兴趣,换成 Apache 或其他 Web Server 也一样。
下面这段代码会监听 9009 端口,并在收到 HTTP 请求后,输出一些信息:
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.write('remoteAddress: ' + req.connection.remoteAddress + '\n');
res.write('x-forwarded-for: ' + req.headers['x-forwarded-for'] + '\n');
res.write('x-real-ip: ' + req.headers['x-real-ip'] + '\n');
res.end();
}).listen(9009, '0.0.0.0');
这段代码除了前面介绍过的 Remote Address 和 X-Forwarded-For,还有一个 X-Real-IP,这又是一个自定义头部字段。X-Real-IP 通常被 HTTP 代理用来表示与它产生 TCP 连接的设备 IP,这个设备可能是其他代理,也可能是真正的请求端。需要注意的是,X-Real-IP 目前并不属于任何标准,代理和 Web 应用之间可以约定用任何自定义头来传递这个信息。
现在可以用域名 + 端口号直接访问这个 Node.js 服务,再配一个 Nginx 反向代理:
location / {
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;
proxy_pass http://127.0.0.1:9009/;
proxy_redirect off;
}
我的 Nginx 监听 80 端口,所以不带端口就可以访问 Nginx 转发过的服务。
测试直接访问 Node 服务:
curl http://t1.imququ.com:9009/ remoteAddress: 114.248.238.236
x-forwarded-for: undefined
x-real-ip: undefined
由于我的电脑直接连接了 Node.js 服务,Remote Address 就是我的 IP。同时我并未指定额外的自定义头,所以后两个字段都是 undefined。
再来访问 Nginx 转发过的服务:
curl http://t1.imququ.com/ remoteAddress: 127.0.0.1
x-forwarded-for: 114.248.238.236
x-real-ip: 114.248.238.236
这一次,我的电脑是通过 Nginx 访问 Node.js 服务,得到的 Remote Address 实际上是 Nginx 的本地 IP。而前面 Nginx 配置中的这两行起作用了,为请求额外增加了两个自定义头:
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
实际上,在生产环境中部署 Web 应用,一般都采用上面第二种方式,有很多好处。但这就引入一个隐患:很多 Web 应用为了获取用户真正的 IP,从 HTTP 请求头中获取 IP。
HTTP 请求头可以随意构造,我们通过 curl 的 -H 参数构造 X-Forwarded-For 和 X-Real-IP,再来测试一把。
直接访问 Node.js 服务:
curl http://t1.imququ.com:9009/ -H 'X-Forwarded-For: 1.1.1.1' -H 'X-Real-IP: 2.2.2.2' remoteAddress: 114.248.238.236
x-forwarded-for: 1.1.1.1
x-real-ip: 2.2.2.2
对于 Web 应用来说,X-Forwarded-For 和 X-Real-IP 就是两个普通的请求头,自然就不做任何处理原样输出了。这说明,对于直连部署方式,除了从 TCP 连接中得到的 Remote Address 之外,请求头中携带的 IP 信息都不能信。
访问 Nginx 转发过的服务:
curl http://t1.imququ.com/ -H 'X-Forwarded-For: 1.1.1.1' -H 'X-Real-IP: 2.2.2.2' remoteAddress: 127.0.0.1
x-forwarded-for: 1.1.1.1, 114.248.238.236
x-real-ip: 114.248.238.236
这一次,Nginx 会在 X-Forwarded-For 后追加我的 IP;并用我的 IP 覆盖 X-Real-IP 请求头。这说明,有了 Nginx 的加工,X-Forwarded-For 最后一节以及 X-Real-IP 整个内容无法构造,可以用于获取用户 IP。
用户 IP 往往会被使用与跟 Web 安全有关的场景上,例如检查用户登录地区,基于 IP 做访问频率控制等等。这种场景下,确保 IP 无法构造更重要。经过前面的测试和分析,对于直接面向用户部署的 Web 应用,必须使用从 TCP 连接中得到的 Remote Address;对于部署了 Nginx 这样反向代理的 Web 应用,在正确配置了 Set Header 行为后,可以使用 Nginx 传过来的 X-Real-IP 或 X-Forwarded-For 最后一节(实际上它们一定等价)。
那么,Web 应用自身如何判断请求是直接过来,还是由可控的代理转发来的呢?在代理转发时增加额外的请求头是一个办法,但是不怎么保险,因为请求头太容易构造了。如果一定要这么用,这个自定义头要够长够罕见,还要保管好不能泄露出去。
判断 Remote Address 是不是本地 IP 也是一种办法,不过也不完善,因为在 Nginx 所处服务器上访问,无论直连还是走 Nginx 代理,Remote Address 都是 127.0.0.1。这个问题还好通常可以忽略,更麻烦的是,反向代理服务器和实际的 Web 应用不一定部署在同一台服务器上。所以更合理的做法是收集所有代理服务器 IP 列表,Web 应用拿到 Remote Address 后逐一比对来判断是以何种方式访问。
通常,为了简化逻辑,生产环境会封掉通过带端口直接访问 Web 应用的形式,只允许通过 Nginx 来访问。那是不是这样就没问题了呢?也不见得。
首先,如果用户真的是通过代理访问 Nginx,X-Forwarded-For 最后一节以及 X-Real-IP 得到的是代理的 IP,安全相关的场景只能用这个,但有些场景如根据 IP 显示所在地天气,就需要尽可能获得用户真实 IP,这时候 X-Forwarded-For 中第一个 IP 就可以排上用场了。这时候需要注意一个问题,还是拿之前的例子做测试:
curl http://t1.imququ.com/ -H 'X-Forwarded-For: unknown, <>"1.1.1.1' remoteAddress: 127.0.0.1
x-forwarded-for: unknown, <>"1.1.1.1, 114.248.238.236
x-real-ip: 114.248.238.236
X-Forwarded-For 最后一节是 Nginx 追加上去的,但之前部分都来自于 Nginx 收到的请求头,这部分用户输入内容完全不可信。使用时需要格外小心,符合 IP 格式才能使用,不然容易引发 SQL 注入或 XSS 等安全漏洞。
结论
- 直接对外提供服务的 Web 应用,在进行与安全有关的操作时,只能通过 Remote Address 获取 IP,不能相信任何请求头;
- 使用 Nginx 等 Web Server 进行反向代理的 Web 应用,在配置正确的前提下,要用
X-Forwarded-For最后一节 或X-Real-IP来获取 IP(因为 Remote Address 得到的是 Nginx 所在服务器的内网 IP);同时还应该禁止 Web 应用直接对外提供服务; - 在与安全无关的场景,例如通过 IP 显示所在地天气,可以从
X-Forwarded-For靠前的位置获取 IP,但是需要校验 IP 格式合法性;
PS:网上有些文章建议这样配置 Nginx,其实并不合理:
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
这样配置之后,安全性确实提高了,但是也导致请求到达 Nginx 之前的所有代理信息都被抹掉,无法为真正使用代理的用户提供更好的服务。还是应该弄明白这中间的原理,具体场景具体分析。
原文地址:https://imququ.com/post/x-forwarded-for-header-in-http.html
[转帖]HTTP X-Forwarded-For 介绍的更多相关文章
- [转帖]MyCat教程【简单介绍】
MyCat教程[简单介绍] 2019-10-15 10:27:23 波波烤鸭 阅读数 618 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. ...
- IE11新文档模式Edge介绍与评估,及在WebBrowser应用中的使用。
浏览器模式与文档模式概念是怎么来的? 1.浏览器模式与文档模式概念起源 为了解决兼容性的问题,在IE浏览器(IE8, IE9, IE10)中,引入了浏览器模式和文档模式两个概念,浏览网页时可以通过按F ...
- opengl入门学习
OpenGL入门学习 说起编程作图,大概还有很多人想起TC的#include <graphics.h>吧? 但是各位是否想过,那些画面绚丽的PC游戏是如何编写出来的?就靠TC那可怜的640 ...
- GJM : Python简单爬虫入门 (一) [转载]
版权声明:本文原创发表于 [请点击连接前往] ,未经作者同意必须保留此段声明!如有侵权请联系我删帖处理! 为大家介绍一个简单的爬虫工具BeautifulSoup BeautifulSoup拥有强大的解 ...
- OpenGL入门学习(转)
OpenGL入门学习 http://www.cppblog.com/doing5552/archive/2009/01/08/71532.html 说起编程作图,大概还有很多人想起TC的#includ ...
- OpenGL理解
说起编程作图,大概还有很多人想起TC的#include <graphics.h>吧? 但是各位是否想过,那些画面绚丽的PC游戏是如何编写出来的?就靠TC那可怜的640*480分辨率.16色 ...
- 分享12款令人瞠目结舌的WebVR演示和实验效果
不管你信不信, WebVR绝对是浏览器下一个让你激动的技术方向, 也许很快你就可以使用VR头显或者相关设备直接访问web内容和资源啦! 在这篇资源分享帖中,我们将介绍很多基于浏览器的VR演示和游戏,帮 ...
- OpenGL入门学习(转载)
说起编程作图,大概还有很多人想起TC的#include <graphics.h>吧? 但是各位是否想过,那些画面绚丽的PC游戏是如何编写出来的?就靠TC那可怜的640*480分辨率.16色 ...
- iphone5s越狱之后必装
一.iphone5s完美越狱之后必装插件一览表 由于iPhone5s配置了强大的64位的A7处理器,所以在iOS7完美越狱后,不少iPhone5s用户发现之前安装的大部分人们插件在越狱后难以兼容.但是 ...
- 魔兽世界serverTrinitycore分析一:前言
一:简单介绍 项目地址:https://github.com/TrinityCore/TrinityCore 帖一段官网介绍吧 TrinityCore is a MMORPG Framework ba ...
随机推荐
- [VMware]ESXI下硬盘的两种直通方式
文章来自:https://rmbz.net/archives/vmware-esxi-passthrough 最近再搞ESXI,把原来的"黑群晖"改成ESXI:因为群晖里有数据,为 ...
- Redis 分片集群
1.Redis分片集群 1.1.搭建分片集群 主从和哨兵可以解决高可用.高并发读的问题.但是依然有两个问题没有解决: 海量数据存储问题 高并发写的问题 使用分片集群可以解决上述问题,如图: 分片集群特 ...
- Pikachu漏洞靶场 PHP反序列化
PHP反序列化 查看源码,以下为关键代码: class S{ var $test = "pikachu"; function __construct(){ echo $this-& ...
- BUUCTF 加固题 Ezsql WriteUp
文章目录 想直接要加固代码的点这里 题目 一.查看 二.进入目标机器加固 修改前的文件: 添加如下代码: 修改后的文件 三.Check 想直接要加固代码的点这里 题目 靶机地址解释: 第一行:目标机器 ...
- ElasticSearch的简单api介绍
1:ElasticSearch是什么? Elasticsearch 是一个分布式的免费开源搜索和分析引擎 适用于包括文本.数字.地理空间.结构化和非结构化数据等在内的所有类型的数据 Elasticse ...
- Programming Abstractions in C阅读笔记:p235-p241
<Programming Abstractions in C>学习第66天,p235-p241总结. 一.技术总结 1.backtracking algorithm(回溯算法) (1)定义 ...
- 记录:websoket切换页面后重复执行问题
问题描述 因为项目需求,实时播放执行信息.而项目的websoket只在这个页面,会有切换情况.从websoket连接得到执行列表数据.断开重连后会传递新连接数据+旧连接数据.也就是说,如果第一次进入页 ...
- 扎根CNCF社区贡献五年是怎样的体验?听听华为云原生开源团队的负责人怎么说
摘要:本月我们要重点介绍王泽锋(Kevin Wang),他是 CNCF 社区的长期贡献者,华为云原生开源团队的负责人,KubeEdge 和 Volcano 项目的联合创始人.Kevin 回答了几个关于 ...
- 如何构建面向海量数据、高实时要求的企业级OLAP数据引擎?
在字节跳动各产品线飞速成长的过程中,对数据分析能力也提出了更高的要求,现有的主流数据分析产品都没办法完全满足业务要求.因此,字节跳动在ClickHouse引擎基础上重构了技术架构,实现了云原生环境的部 ...
- PPT 光效果
点状.线状.面状.光影 "光" = PPT高大上的秘密