项目中需要判断发起微信支付回调的IP在不在微信商户出口IP中,以防止私钥泄露别人发起伪装过的假回调造成损失
/**
* @explain 获取用户IP
* @param int $type
* @return mixed
*/
function get_client_ip($type = 0)
{
$type = $type ? 1 : 0;
static $ip = NULL;
if ($ip !== NULL) return $ip[$type];
if (isset($_SERVER['HTTP_X_REAL_IP'])) {//nginx 代理模式下,获取客户端真实IP
$ip = $_SERVER['HTTP_X_REAL_IP'];
} elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {//客户端的ip
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {//浏览当前页面的用户计算机的网关
$arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
$pos = array_search('unknown', $arr);
if (false !== $pos) unset($arr[$pos]);
$ip = trim($arr[0]);
} elseif (isset($_SERVER['REMOTE_ADDR'])) {
$ip = $_SERVER['REMOTE_ADDR'];//浏览当前页面的用户计算机的ip地址
} else {
$ip = $_SERVER['REMOTE_ADDR'];
}
// IP地址合法验证
$long = sprintf("%u", ip2long($ip));
$ip = $long ? array($ip, $long) : array('0.0.0.0', 0);
return $ip[$type];
}
/**
* Notes: 判断IP在不在IP网段内
* Date: 2019/6/18
* Time: 15:22
* @param $ip 要查询的IP地址
* @param $network IP段 例: 192.168.1.1/24
* @return bool
*/
function ip_in_network($ip, $network)
{
$ip = (double) (sprintf("%u", ip2long($ip)));
$s = explode('/', $network);
$network_start = (double) (sprintf("%u", ip2long($s[0])));
$network_len = pow(2, 32 - $s[1]);
$network_end = $network_start + $network_len - 1;
if ($ip >= $network_start && $ip <= $network_end) {
return true;
}
return false;
}
验证不通过则拒绝请求
class Notify extends Controller
{
/**
* 判断当前发起通知IP是否在微信商户出口IP段里,防止私钥泄露伪造通知
* Notify constructor.
* @param Request $request
*/
public function __construct(Request $request)
{
$ip = get_client_ip();
$whiteList = Setting::get('WX_NOTIFY_WHITE_LIST')->conf_value ?? '';
$whiteList = explode(',',$whiteList);//ip白名单
$verify = 0;
foreach ($whiteList as $k => $v)
{
$verify = ip_in_network($ip,$v);
if ($verify == 1)
break;
}
$verify == 0 && exit('Access Denied');
}