PHP安全:如何正确的取得使用者 IP?

很多网站都会有侦测用户 IP 的功能,不管是判断使用者来自哪边,或者是记录用户的位置。但是你知道吗?网络上大多数的教学全部都是「错误」的。正确的程序写法可以确保知道访客的 IP,但是错误的写法却可能让网站管理者永远不知道犯罪者的来源。

这次我们单就侦测 IP 的议题来探讨各种错误的写法。

你知道网络上的教学是不安全的吗?

我们先来看一下网络上的教学,让我们 Google 找一下「PHP 取得 IP」,就可以看到许多人热心的教学,我们随意挑一个常见的教学来看看。

以 PHP 为例:

<?php

if(!empty($_SERVER['HTTP_CLIENT_IP'])){

$myip
=
$_SERVER['HTTP_CLIENT_IP'];

}else
if(!empty($_SERVER['HTTP_X_FORWARDED_FOR'])){

$myip
=
$_SERVER['HTTP_X_FORWARDED_FOR'];

}else{

$myip=
$_SERVER['REMOTE_ADDR'];

}

echo
$myip;

?>

以 ASP.NET 为例:

Dim ClientIP As String = Request.ServerVariables("HTTP_X_FORWARDED_FOR")

IF ClientIP = String.Empty Then

ClientIP = Request.ServerVariables("REMOTE_ADDR")

End IF

这是一个很基本的写法、很正确的想法,如果 HTTP Header 中包含「Client-IP」,就先以他当作真实 IP。若包含「X-Forwarded-For」,则取他当作真实 IP。若两者都没有,则取「REMOTE_ADDR」变量作为真实 IP。因为当用户联机时透过代理服务器时,REMOTE_ADDR 会显示为代理服务器 Proxy 的 IP。部分代理服务器会将用户的原始真实 IP 放在 Client-IP 或 X-Forwarded-For header 中传递,如果在变量中呼叫则可以取得真实 IP。

但是你知道吗?网络上 80% 的教学写法全部都是「错误」的。

为什么这样说呢?请大家记得一件事情:「任何从客户端取得的资料都是不可信任的!

窜改 HTTP Header

「X-Forwarded-For」这个变量虽然「有机会」取得使用者的真实 IP,但是由于这个值是从客户端传送过来的,所以「有可能」被使用者窜改。

举例来说,我写了一个小程序,侦测这些常见的 HTTP Header 判断 IP。并且使用 Burp Suite 这个工具来修改 HTTP Request。

页面上显示目前我目前的 IP「49.50.68.17」,并且其他的 header 是空的。但如果我今天使用 Burp Suite 之类的 Proxy 工具自行窜改封包,加上 X-Forwarded-For 或是 Client-IP header:

修改完毕之后,再到原本的显示 IP 接口,会发现网页错将我窜改的 header 当作正确的数据填入。

使用代理服务器 Proxy 的情况

使用代理服务器的情况下,HTTP Header 会有不同的行为。例如 Elite Proxy 如何隐藏客户端的真实 IP。以下简单介绍几种常见的状况给各位参考。

直接联机
(没有使用 Proxy)

  • REMOTE_ADDR: 客户端真实 IP
  • HTTP_VIA: 无
  • HTTP_X_FORWARDED_FOR: 无

Transparent Proxy

  • REMOTE_ADDR: 最后一个代理服务器 IP
  • HTTP_VIA: 代理服务器 IP
  • HTTP_X_FORWARDED_FOR: 客户端真实 IP,后以逗点串接多个经过的代理服务器 IP

Anonymous Proxy

  • REMOTE_ADDR: 最后一个代理服务器 IP
  • HTTP_VIA: 代理服务器 IP
  • HTTP_X_FORWARDED_FOR: 代理服务器 IP,后以逗点串接多个经过的代理服务器 IP

High Anonymity Proxy (Elite Proxy)

  • REMOTE_ADDR: 代理服务器 IP
  • HTTP_VIA: 无
  • HTTP_X_FORWARDED_FOR: 无 (或以逗点串接多个经过的代理服务器 IP)

实际情况

在我们测试的过程中,通常我们都会让浏览器自带 X-Forwarded-For,并且自行填入 IP。常常会发现有一些网站出现如下的警告…

有没有搞错?「上次登入位置 127.0.0.1」?没错,这个是知名论坛套件「Discuz!」的功能,抓取 IP 的功能也是不安全的写法。也有这样的经验,之前开着 X-Forwarded-For 的 header 到一些网站,竟然直接出现管理者后台!

你觉得只有一般人撰写的程序会有这样的问题吗?其实大型网站也可能会有类似的问题:

先不论为什么 127.0.0.1 会在美国,这样的写法可能会让管理者永远抓不到犯罪者的真实 IP,甚至攻击者可以窜改 header 插入特殊字符,对网站进行 SQL Injection 或者 Cross-Site Scripting 攻击。

正确又安全的方式

任何从客户端取得的资料都是不可信任的!

请各位开发者、管理者记住这个大原则,虽然这些 Request Header 可能含有真实 IP 的信息,但是因为他的安全性不高,因此我们绝对不能完全信赖这个数值。

那我们该怎么处理呢?我的建议是记录所有相关的 header 字段存入数据库,包含「REMOTE_ADDR」「X-Forwarded-For」等等,真正有犯罪事件发生时,就可以调出所有完整的 IP 信息进行人工判断,找出真正的 IP。当然从 header 存入的数值也可能会遭到攻击者窜改插入特殊字符尝试 SQL Injection,因此存入值必须先经过过滤,或者使用 Prepared Statement 进行存放。

可以参考的 HTTP Header(依照可能存放真实 IP 的顺序)

  • HTTP_CLIENT_IP
  • HTTP_X_FORWARDED_FOR
  • HTTP_X_FORWARDED
  • HTTP_X_CLUSTER_CLIENT_IP
  • HTTP_FORWARDED_FOR
  • HTTP_FORWARDED
  • REMOTE_ADDR (真实 IP 或是 Proxy IP)
  • HTTP_VIA (参考经过的 Proxy)

「黑客思维」就是找出网站任何可能窜改的弱点,从网页上的元素到 HTTP Header 都是尝试的对象。因此身为防御者一定要清楚的知道哪些数值是不能信赖的,不要再参考网络上错误的教学了!

来源:http://devco.re/blog/2014/06/19/client-ip-detection/

PHP安全:如何正确的取得使用者 IP?的更多相关文章

  1. 【翻译自mos文章】怎么正确的计算一个ip地址的subnet id?

    怎么正确的计算一个ip地址的subnet id? 来源于: How to calculate the correct subnet for an interface (文档 ID 1059759.1) ...

  2. VM中ubuntu已经正确配置了静态IP仍无法上网

    情况描述:正确配置了ubuntu的IP,getway,DNS..无法ping通getway. 环境:宿主机:win7 32Bit  虚拟机:ununtu 10.04  VM:9.0.1 build-8 ...

  3. 微信支付报ip错,怀疑是因为不能正确获取$_Server[addr])ip导致的

    报如下错误,应该是本地测试环境不能正确获取客户ip导致的错误 果然 放到服务器上在测试就好了

  4. nginx反向代理下thinkphp、php获取不到正确的外网ip

    在记录用户发送短信需要获取用户ip时,tp一直获取的是内网ip:10.10.10.10 tp框架获取ip方法:get_client_ip /** * 获取客户端IP地址 * @param intege ...

  5. Java正确获取客户端真实IP方法整理

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

  6. 干货:Java正确获取客户端真实IP方法整理

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

  7. C# 验证IP是否正确简易方法 源代码

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  8. Asp.Net Core2.0获取客户IP地址,及解决发布到Ubuntu服务器获取不到正确IP解决办法

    1.获取客户端IP地址实现方法(扩展类) using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.ModelBinding; u ...

  9. TCP/IP 和 Socket 的关系

    要写网络程序就必须用Socket,这是程序员都知道的.而且,面试的时候,我们也会问对方会不会Socket编程?一般来说,很多人都会说,Socket编程基本就是listen,accept以及send,w ...

随机推荐

  1. 洛谷P4587 [FJOI2016]神秘数(主席树)

    题面 洛谷 题解 考虑暴力,对于询问中的一段区间\([l,r]\),我们先将其中的数升序排序,假设当前可以表示出\([1,k]\)目前处理\(a_i\),假如\(a_i>k+1\),则答案就是\ ...

  2. UGUI的优点新UI系统三效率高效果好

    UGUI的优点新UI系统三效率高效果好 通过对批处理(batching).纹理图集(texture atlasing)和新的canvas组件的支持,新UI系统提供了一个经过优化的解决方案,使得开发者添 ...

  3. VB程序打包方法之如何在发布安装之后不带源码

    很久之前,我发表了一片博客是VB程序如何打包,在那里面我总结了两个方法.有兴趣可以看看我的这篇博客http://blog.csdn.net/lu930124/article/details/88467 ...

  4. javascrip 求最大公因数(分解质数法)发生的问题

    //这是求一个数的质因数,例如:12=2*2*3 其中2,3都是质数.function primeArray(n, array) { array = new Array(); for (var i = ...

  5. 【状态压缩DP】NOIP2005-river过河

    [问题描述] 在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧.在桥上有一些石子,青蛙很讨厌踩在这些石子上.由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看 ...

  6. Activity(活动)生命周期--项目测试

    一.新建一个Activitylifetest项目,允许Android Studio帮我们自动创建活动和布局,并且勾选Luancher Activity来将创建的活动设置为主活动. 可以参考: http ...

  7. 【MySQL笔记】触发器,存储过程和函数

    一.触发器 触发器(TRIGGER):是由事件来触发某个操作.当数据库系统执行这些事件时,就会激活触发器执行相应的操作.MySQL从5.0.2版本开始支持触发器. 触发事件:INSERT语句.UPDA ...

  8. JAVA EE 项目常用知识 之AJAX技术实现select下拉列表联动的两种用法(让你真正理解ajax)

    ajax 下拉列表联动的用法. ajax的定义: AJAX 是一种用于创建快速动态网页的技术. 通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新.这意味着可以在不重新加载整个网页的 ...

  9. 通过现有数据导出新表SQL

    Date: 20140217 Auth: JIN 需求: 导出一个表的两个列的表的SQL语句(包含数据) 方法:创立一个临时表 mysql> desc kw_keywords;+-------- ...

  10. CentOS 6.9下KVM虚拟机网络Bridge(网桥)方式与NAT方式详解(转)

    摘要:KVM虚拟机网络配置的两种方式:NAT方式和Bridge方式.Bridge方式的配置原理和步骤.Bridge方式适用于服务器主机的虚拟化.NAT方式适用于桌面主机的虚拟化. NAT的网络结构图: ...