nginx反向代理时如何传递真实客户端IP

背景

nginx作为反向代理的时候,是用自己的IP去和服务器建立TCP连接,导致业务服务器无法看到通过ng反代访问的客户端的真实IP

  • 业务服务器

在8040端口起了一个web服务,并尝试去从请求中的X-Real-IP头中获取真实IP,如果没有X-Real-IP头的话,获取X-Forwarded-For最右边的IP作为源IP。如果两个都没有的话则使用和我们建立TCP连接的IP。并输出日志到web_service.log文件里

import http.server  
import socketserver  
import logging  
  
# 配置日志  
logging.basicConfig(filename='web_service.log', level=logging.INFO,  
                    format='%(asctime)s - %(levelname)s - %(message)s')  
  
class MyHTTPRequestHandler(http.server.BaseHTTPRequestHandler):  
    def do_GET(self):  
        # 尝试从X-Real-IP和X-Forwarded-For获取IP  
        real_ip = self.headers.get('X-Real-IP')  
        forwarded_for = self.headers.get('X-Forwarded-For')  
  
        # 如果没有X-Real-IP,则尝试使用X-Forwarded-For  
        if not real_ip:  
            if forwarded_for:  
            # 分割字符串,取最后一个元素(即最右边的IP),并去除可能的前后空格  
               real_ip = forwarded_for.split(',')[-1].strip()    

        # 如果两者都没有,则使用远程地址(可能是代理的地址)  
        if not real_ip:  
            real_ip = self.client_address[0]  
  
        # 记录访问日志  
        logging.info(f'Access from IP: {real_ip}, Path: {self.path}')  
  
        # 构建响应消息  
        response_message = "你干嘛,哎哟! Your IP is: " + real_ip  
  
        # 发送响应  
        self.send_response(200)  
        self.send_header('Content-type', 'text/html; charset=utf-8') 
        self.end_headers()  
  
        # 将响应消息编码为字节串并发送  
        self.wfile.write(response_message.encode('utf-8'))  


    def log_message(self, format, *args):  
        # 重写日志方法,因为我们已经自定义了日志记录  
        pass  
  
def run(server_class=http.server.HTTPServer, handler_class=MyHTTPRequestHandler):  
    server_address = ('', 8040)  
    httpd = server_class(server_address, handler_class)  
    logging.info('Starting httpd...\n')  
    try:  
        httpd.serve_forever()  
    except KeyboardInterrupt:  
        pass  
    httpd.server_close()  
    logging.info('Stopping httpd...\n')  
  
if __name__ == '__main__':  
    run()

NG测试

不过NG测试

此时拓扑如下:

客户端----->服务器

  • 不伪造XFF和x-real

可以看到,服务器获取到的是真实的客户端IP

  • 伪造x-real-ip

此时服务器获取到的IP是从X-Real-Ip这个头里获取的,但是这个头是我伪造的,导致服务器没有获取到我真实的IP

  • 伪造X-forwarded-for头

可以看到服务器获取到的源IP是XFF的最右边的一个,但是这个也是我伪造的

  • 结论

如果业务服务器从x-real-ip或者xff头中获取,客户端一就无法获取到真实的源IP了

过NG测试

  • nginx配置文件

upstream www.server_pools {
         server 192.168.1.35:8040 ;    
}
server {  
        listen       9090;  
        server_name  localhost;  
        location / {  
            proxy_pass http://www.server_pools;       
            proxy_set_header Host $host;  
            proxy_set_header X-Real-IP $remote_addr;       #将客户端的真实IP地址添加到X-Real-IP头中
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;   #将客户端的IP地址(以及可能经过的其他代理的IP地址)添加到X-Forwarded-For头中,形成一个由逗号分隔的IP地址列表
            proxy_set_header X-Forwarded-Proto $scheme;     #递原始请求使用的协议(http或https)
      }  
}
  • nginx不配置任何字段

upstream www.server_pools {
         server 192.168.1.35:8040 ;    
}
server {  
        listen       9090;  
        server_name  localhost;  
        location / {  
            proxy_pass http://www.server_pools;       
      }  
}

可以看到因为请求里面没有x-real-ip和xff头且ng也没有去set这两个头,业务服务器读不到这两个头自然而然地获取到的IP就是remote ip即ng的ip

  • nginx配置X-real-IP头

upstream www.server_pools {
         server 192.168.1.35:8040 ;    
}
server {  
        listen       9090;  
        server_name  localhost;  
        location / {  
            proxy_pass http://www.server_pools;  
            proxy_set_header Host $host;  
            proxy_set_header X-Real-IP $remote_addr;
      }  
}

客户端不伪造的情况下

获取到的是真实的客户端IP,且此时在服务器抓包看到ng发过来的包里是有x-real-ip这个头的

客户端伪造x-real-ip

可以看到此时获取到的IP依然是客户端的IP,在服务器抓包看到的X-Real-IP也是客户端的IP,当nginx收到客户端的请求中时,会把X-Real-IP的内容用remote IP替换掉

  • nginx配置XFF头

upstream www.server_pools {
         server 192.168.1.35:8040 ;    
}
server {  
        listen       9090;  
        server_name  localhost;  
        location / {  
            proxy_pass http://www.server_pools;       
            proxy_set_header Host $host;  
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;   #将客户端的IP地址(以及可能经过的其他代理的IP地址)添加到X-Forwarded-For头中,形成一个由逗号分隔的IP地址列表
            proxy_set_header X-Forwarded-Proto $scheme;     #递原始请求使用的协议(http或https)
      }  
}

客户端不伪造XFF头

获取到的是真实的客户端IP,nginx在转发请求的时候把客户端的IP塞到了XFF头里转发给服务器,服务器在读不到X-Real-IP的时候会去读XFF,此时输出的IP即为XFF里的真实IP

  • 客户端伪造XFF头

此时获取到的源 IP依然是客户端的真实IP,请求咋经过ng的时候,ng会把客户端的真实IP插到XFF头的最右边

结论

请求不过NG的时候,客户端伪造X-Real-IP头和XFF头如果业务服务器是从这两个头中读源IP的话很容易导致业务服务器获取不到真实的客户端IP所以只能获取remote IP作为源IP,当请求经过NG代理的话且业务服务器获取remote IP作为源IP的话会导致获取到的源IP都是NG的IP。所以需要调整NG的配置主动去set x-real和xff头并把remote IP插进去,以及调整业务服务器获取源IP的方式

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值