泰山付支付场景需要获取用户在商家侧下单 IP(clientIp),用于提高支付安全性。此指引用于帮助商家获取正确且真实的 clientIp 字段。
注意: 商家侧下单 IP 指的是在发起泰山付支付流程之前,用户在商家侧发起下单操作生成商家订单时的客户端 IP,并非请求泰山付支付网关进入支付流程后的客户端 IP。
不同商家由于部署网络环境不同,获取商家侧下单 IP 的方式也存在差异,商家可以根据自身情况获取真实的用户下单 IP。常见的场景有两种分为 有代理 和 无代理。
商家的网络部署环境未使用代理服务器,用户直接与商家服务端通信。这种情况较为简单直接获取客户端 IP 即可。
public String getMcCreateTradeIp(HttpServletRequest request) {
String mc_create_trade_ip = "/";
if(!request.getRemoteAddr().equals("")){
mc_create_trade_ip = request.getRemoteAddr();
};
return mc_create_trade_ip;
}
function get_mcCreateTradeIp(){
mc_create_trade_ip = "/";
if ($_SERVER['REMOTE_ADDR']){
$mc_create_trade_ip = $_SERVER['REMOTE_ADDR']
}elseif (getenv("REMOTE_ADDR")){
$mc_create_trade_ip = getenv("REMOTE_ADDR");
}
return $mc_create_trade_ip;
}
当您的服务器未经过代理,商家在获取用户创单IP时,不要去获取用户请求的 HTTP 头中 X-Forwarded-For 、Client-IP 、X-Client-IP、 X-Originating-IP 、X-Real-IP 、X-Remote-Addr、X-Remote-IP 等字段,而是直接获取当前跟自己建连的 IP 地址。在许多三方库中,可能会自己获取 X-Forwarded-For 中的地址用来代替建连地址,从而导致IP可以被任意伪造,引起防护失效。
由于商家的业务需求使用代理服务器,用户先与代理服务器通信,代理服务器再访问商家服务端。这种情况下直接获取客户端 IP 会获取到代理服务器的 IP,就会出现 127.0.0.1、localhost 或者内网 IP 等情况。服务端无法获取到用户的真实 IP,可以通过配置代理服务器来记录用户的真实 IP。
存在 CDN 的情况下,检查 CDN 的配置情况,确保 CDN 可以将客户端真实 IP 正确回传,同时防止 X-Forwarded-For 等字段被伪造的情况发生。
nginx有代理情况配置
根据需求修改nginx配置文件,向请求头添加字段用于记录客户端真实IP。
#修改default.conf配置文件
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Real-Port $remote_port;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
apache有代理情况配置
#修改httpd.conf文件
vi /usr/local/apache/conf/httpd.conf
Include conf/extra/httpd-remoteip.conf
# 加载mod_remoteip.so模块
LoadModule remoteip_module modules/mod_remoteip.so
RemoteIPHeader X-Forwarded-For
RemoteIPInternalProxy <ip_range1> <ip_range2> …… <ip_rangex>
其它中间件可根据需求自行配置,记录通信过程。后端服务器代码根据代理服务器部署情况获取客户端真实 IP。
//获取请求头中的真实IP
public String getMcCreateTradeIp(HttpServletRequest request) {
//...其他逻辑
String mc_create_trade_ip = "/";
String mcProxyIp = "/";
String mcXffIp = "/";
if(!request.getRemoteAddr().equals("")){
mc_create_trade_ip = request.getRemoteAddr();//根据真实情况获取
};
if (!request.getHeader("X-Real-IP").equals("")){
mcProxyIp=request.getHeader("X-Real-IP");
mc_create_trade_ip = mcProxyIp;//根据真实情况获取
}else if(!request.getHeader("X-Forwarded-For").equals("")){
String[] mcXffIps = request.getHeader("X-Forwarded-For").split(",");
mc_create_trade_ip =mcXffIps[0];//根据真实情况获取
}else{
mc_create_trade_ip="/";
}
//...其他逻辑,根据真实情况获取真实IP
return mc_create_trade_ip;
}
注意:当服务器经过代理,商户在获取创单 IP 时,可判断自己真实的代理服务器 IP 在请求头中的添加情况,获取与最外层代理服务器建立通信的真实客户端 IP。如果出现 X-Forwarded-For 中 IP 列表与真实情况不同很大可能是伪造的请求头参数。
POST /test HTTP/1.1
Host: aaa.bb.cn
Cookie: xxxx
Content-Length: 1420
Cache-Control: max-age=0
Sec-Ch-Ua: " Not A;Brand";v="99", "Chromium";v="102", "Google Chrome";v="102"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Upgrade-Insecure-Requests: 1
Origin: https://aaa.bb.cn
Content-Type: application/x-www-form-urlencoded
X-Forwarded-For: 120.24.169.223,8.8.8.8
Client-IP: 120.24.169.22
X-Client-IP: 120.24.169.22
X-Originating-IP: 120.24.169.22
X-Real-IP: 120.24.169.22
X-Remote-Addr: 120.24.169.22
X-Remote-IP: 120.24.169.22
User-Agent: Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.114 Mobile Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-site
Sec-Fetch-Mode: navigate
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate
Accept-Language: en,zh-CN;q=0.9,zh;q=0.8
Connection: close
案例中:
• 无代理的情况下,如果获取的 IP 地址是 120.24.169.223 或者 120.24.169.22 或者 8.8.8.8,则代表程序中存在 bug。
• 有代理的情况下,如果 X-Forwarded-For: 120.24.169.223,8.8.8.8 与真实网络线路路由顺序不同,证明此请求存在伪造 IP,请谨慎获取,测试确保正确后再回传。