Configuring Laravel for Reverse Proxy - php

I have to upload a website that uses Laravel.
The server I must use are using a reverse proxy and when I put the files i developped on my computer, I'm getting a DNS error.
I do not have access to the server configuration, I can only upload/download files on the website's server partition.
I searched to find a solution but anything I can find was sort of related to this question.

So, this is not Laravel version, but this can help you, I hope !
Here is some code I wrote in cakePHP 2.X because I have some issues with a reverse proxy too
Cache class is the one from cakePHP, it's very simple (60 sec expiration, automatically serialized data).
LogError function is a simple log function
The code is the following
// Constants
define('REVERSE_PROXY_ADDR' , 'r-proxy.internal-domain.com');
define('REVERSE_PROXY_CACHE', 'r-proxy');
// Cache Config
Cache::config(REVERSE_PROXY_CACHE, array(
'engine' => 'File',
'prefix' => REVERSE_PROXY_CACHE . '_',
'path' => CACHE . REVERSE_PROXY_CACHE . '/',
'serialize' => true,
'duration' => 60,
));
function clientIp()
{
// Originaly from CakePHP 2.X
// ------------------------------
if ( isset($_SERVER['HTTP_CLIENT_IP']) )
{
$ipaddr = $_SERVER['HTTP_CLIENT_IP'];
}
else
{
$ipaddr = $_SERVER['REMOTE_ADDR'];
}
if ( isset($_SERVER['HTTP_CLIENTADDRESS']) )
{
$tmpipaddr = $_SERVER['HTTP_CLIENTADDRESS'];
if ( !empty( $tmpipaddr ) )
{
$ipaddr = preg_replace('/(?:,.*)/', '', $tmpipaddr);
}
}
$ip = trim($ipaddr);
// ------------------------------
// Reverse proxy stuff
if ( defined('REVERSE_PROXY_ADDR') && defined('REVERSE_PROXY_CACHE') )
{
$xfor = preg_replace('/(?:,.*)/', '', $_SERVER['HTTP_X_FORWARDED_FOR']);
$list = Cache::read('iplist', REVERSE_PROXY_CACHE);
if ( $list === false )
{
$list = gethostbynamel(REVERSE_PROXY_ADDR);
Cache::write('iplist', $list, REVERSE_PROXY_CACHE);
}
// empty or unreadable
if ( empty( $list ) )
{
logError('Unable to gather reverse proxy ip list, or empty list');
logError('Type : ' . gettype($list));
logError('IP : ' . $ip . ' - X-FORWARDED-FOR : ' . $xfor);
return $ip;
}
// not array ?!?!
if ( !is_array($list) )
{
logError('Given list was not an array');
logError('Type : ' . gettype($list));
logError($list);
return $ip;
}
// if in array, give forwarded for header
if ( in_array($ip, $list, true) )
{
return $xfor;
}
}
return $ip;
}
Then you just have to call the clientIp(); function.
If you have a static IP Address for your reverse proxy, you can just set it manually in the code, but that's not a good practice. But you will not need to use cache, and it will simplify a lot the code.
If you use a dynamic reverse proxy, you will have to query it on its hostname like this (what I did in the posted code) :
gethostbynamel('reverse-proxy-addr') to get a list of possible rproxy IPs
OR
gethostbyname('reverse-proxy-addr') to get one IP for rproxy
In other case you just have to check that REMOTE_ADDR is in a list of IPs marked as Reverse-proxy IPs
Hope it helps !

Related

PHP SoapServer - get caller's IP [duplicate]

I know there are a plethora of $_SERVER variables headers available for IP address retrieval. I was wondering if there is a general consensus as to how to most accurately retrieve a user's real IP address (well knowing no method is perfect) using said variables?
I spent some time trying to find an in depth solution and came up with the following code based on a number of sources. I would love it if somebody could please poke holes in the answer or shed some light on something perhaps more accurate.
edit includes optimizations from #Alix
/**
* Retrieves the best guess of the client's actual IP address.
* Takes into account numerous HTTP proxy headers due to variations
* in how different ISPs handle IP addresses in headers between hops.
*/
public function get_ip_address() {
// Check for shared internet/ISP IP
if (!empty($_SERVER['HTTP_CLIENT_IP']) && $this->validate_ip($_SERVER['HTTP_CLIENT_IP']))
return $_SERVER['HTTP_CLIENT_IP'];
// Check for IPs passing through proxies
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
// Check if multiple IP addresses exist in var
$iplist = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
foreach ($iplist as $ip) {
if ($this->validate_ip($ip))
return $ip;
}
}
}
if (!empty($_SERVER['HTTP_X_FORWARDED']) && $this->validate_ip($_SERVER['HTTP_X_FORWARDED']))
return $_SERVER['HTTP_X_FORWARDED'];
if (!empty($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']) && $this->validate_ip($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']))
return $_SERVER['HTTP_X_CLUSTER_CLIENT_IP'];
if (!empty($_SERVER['HTTP_FORWARDED_FOR']) && $this->validate_ip($_SERVER['HTTP_FORWARDED_FOR']))
return $_SERVER['HTTP_FORWARDED_FOR'];
if (!empty($_SERVER['HTTP_FORWARDED']) && $this->validate_ip($_SERVER['HTTP_FORWARDED']))
return $_SERVER['HTTP_FORWARDED'];
// Return unreliable IP address since all else failed
return $_SERVER['REMOTE_ADDR'];
}
/**
* Ensures an IP address is both a valid IP address and does not fall within
* a private network range.
*
* #access public
* #param string $ip
*/
public function validate_ip($ip) {
if (filter_var($ip, FILTER_VALIDATE_IP,
FILTER_FLAG_IPV4 |
FILTER_FLAG_IPV6 |
FILTER_FLAG_NO_PRIV_RANGE |
FILTER_FLAG_NO_RES_RANGE) === false)
return false;
self::$ip = $ip;
return true;
}
Words of Warning (update)
REMOTE_ADDR still represents the most reliable source of an IP address. The other $_SERVER variables mentioned here can be spoofed by a remote client very easily. The purpose of this solution is to attempt to determine the IP address of a client sitting behind a proxy. For your general purposes, you might consider using this in combination with the IP address returned directly from $_SERVER['REMOTE_ADDR'] and storing both.
For 99.9% of users this solution will suit your needs perfectly. It will not protect you from the 0.1% of malicious users looking to abuse your system by injecting their own request headers. If relying on IP addresses for something mission critical, resort to REMOTE_ADDR and don't bother catering to those behind a proxy.
Here is a shorter, cleaner way to get the IP address:
function get_ip_address(){
foreach (array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR') as $key){
if (array_key_exists($key, $_SERVER) === true){
foreach (explode(',', $_SERVER[$key]) as $ip){
$ip = trim($ip); // just to be safe
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false){
return $ip;
}
}
}
}
}
Your code seems to be pretty complete already, I cannot see any possible bugs in it (aside from the usual IP caveats), I would change the validate_ip() function to rely on the filter extension though:
public function validate_ip($ip)
{
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false)
{
return false;
}
self::$ip = sprintf('%u', ip2long($ip)); // you seem to want this
return true;
}
Also your HTTP_X_FORWARDED_FOR snippet can be simplified from this:
// check for IPs passing through proxies
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
{
// check if multiple ips exist in var
if (strpos($_SERVER['HTTP_X_FORWARDED_FOR'], ',') !== false)
{
$iplist = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
foreach ($iplist as $ip)
{
if ($this->validate_ip($ip))
return $ip;
}
}
else
{
if ($this->validate_ip($_SERVER['HTTP_X_FORWARDED_FOR']))
return $_SERVER['HTTP_X_FORWARDED_FOR'];
}
}
To this:
// check for IPs passing through proxies
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
{
$iplist = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
foreach ($iplist as $ip)
{
if ($this->validate_ip($ip))
return $ip;
}
}
You may also want to validate IPv6 addresses.
Even then however, getting a user's real IP address is going to be unreliable. All they need to do is use an anonymous proxy server (one that doesn't honor the headers for http_x_forwarded_for, http_forwarded, etc) and all you get is their proxy server's IP address.
You can then see if there is a list of proxy server IP addresses that are anonymous, but there is no way to be sure that is 100% accurate as well and the most it'd do is let you know it is a proxy server. And if someone is being clever, they can spoof headers for HTTP forwards.
Let's say I don't like the local college. I figure out what IP addresses they registered, and get their IP address banned on your site by doing bad things, because I figure out you honor the HTTP forwards. The list is endless.
Then there is, as you guessed, internal IP addresses such as the college network I metioned before. A lot use a 10.x.x.x format. So all you would know is that it was forwarded for a shared network.
Then I won't start much into it, but dynamic IP addresses are the way of broadband anymore. So. Even if you get a user IP address, expect it to change in 2 - 3 months, at the longest.
We use:
/**
* Get the customer's IP address.
*
* #return string
*/
public function getIpAddress() {
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
return $_SERVER['HTTP_CLIENT_IP'];
} else if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
return trim($ips[count($ips) - 1]);
} else {
return $_SERVER['REMOTE_ADDR'];
}
}
The explode on HTTP_X_FORWARDED_FOR is because of weird issues we had detecting IP addresses when Squid was used.
My answer is basically just a polished, fully-validated, and fully-packaged, version of #AlixAxel's answer:
<?php
/* Get the 'best known' client IP. */
if (!function_exists('getClientIP'))
{
function getClientIP()
{
if (isset($_SERVER["HTTP_CF_CONNECTING_IP"]))
{
$_SERVER['REMOTE_ADDR'] = $_SERVER["HTTP_CF_CONNECTING_IP"];
};
foreach (array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR') as $key)
{
if (array_key_exists($key, $_SERVER))
{
foreach (explode(',', $_SERVER[$key]) as $ip)
{
$ip = trim($ip);
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false)
{
return $ip;
};
};
};
};
return false;
};
};
$best_known_ip = getClientIP();
if(!empty($best_known_ip))
{
$ip = $clients_ip = $client_ip = $client_IP = $best_known_ip;
}
else
{
$ip = $clients_ip = $client_ip = $client_IP = $best_known_ip = '';
};
?>
Changes:
It simplifies the function name (with 'camelCase' formatting style).
It includes a check to make sure the function isn't already declared in another part of your code.
It takes into account 'CloudFlare' compatibility.
It initializes multiple "IP-related" variable names to the returned value, of the 'getClientIP' function.
It ensures that if the function doesn't return a valid IP address, all the variables are set to a empty string, instead of null.
It's only (45) lines of code.
The biggest question is for what purpose?
Your code is nearly as comprehensive as it could be - but I see that if you spot what looks like a proxy added header, you use that INSTEAD of the CLIENT_IP, however if you want this information for audit purposes then be warned - its very easy to fake.
Certainly you should never use IP addresses for any sort of authentication - even these can be spoofed.
You could get a better measurement of the client ip address by pushing out a flash or java applet which connects back to the server via a non-http port (which would therefore reveal transparent proxies or cases where the proxy-injected headers are false - but bear in mind that, where the client can ONLY connect via a web proxy or the outgoing port is blocked, there will be no connection from the applet.
Thanks for this, very useful.
It would help though if the code were syntactically correct. As it is there's a { too many around line 20. Which I'm afraid means nobody actually tried this out.
I may be crazy, but after trying it on a few valid and invalid addresses, the only version of validate_ip() that worked was this:
public function validate_ip($ip)
{
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE) === false)
return false;
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE) === false)
return false;
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false && filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false)
return false;
return true;
}
Here's a modified version if you use CloudFlare caching layer Services
function getIP()
{
$fields = array('HTTP_X_FORWARDED_FOR',
'REMOTE_ADDR',
'HTTP_CF_CONNECTING_IP',
'HTTP_X_CLUSTER_CLIENT_IP');
foreach($fields as $f)
{
$tries = $_SERVER[$f];
if (empty($tries))
continue;
$tries = explode(',',$tries);
foreach($tries as $try)
{
$r = filter_var($try,
FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 |
FILTER_FLAG_NO_PRIV_RANGE |
FILTER_FLAG_NO_RES_RANGE);
if ($r !== false)
{
return $try;
}
}
}
return false;
}
Just another clean way:
function validateIp($var_ip){
$ip = trim($var_ip);
return (!empty($ip) &&
$ip != '::1' &&
$ip != '127.0.0.1' &&
filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false)
? $ip : false;
}
function getClientIp() {
$ip = #$this->validateIp($_SERVER['HTTP_CLIENT_IP']) ?:
#$this->validateIp($_SERVER['HTTP_X_FORWARDED_FOR']) ?:
#$this->validateIp($_SERVER['HTTP_X_FORWARDED']) ?:
#$this->validateIp($_SERVER['HTTP_FORWARDED_FOR']) ?:
#$this->validateIp($_SERVER['HTTP_FORWARDED']) ?:
#$this->validateIp($_SERVER['REMOTE_ADDR']) ?:
'LOCAL OR UNKNOWN ACCESS';
return $ip;
}
i realize there are much better and more concise answers above, and this isnt a function nor the most graceful script around. In our case we needed to output both the spoofable x_forwarded_for and the more reliable remote_addr in a simplistic switch per-say. It needed to allow blanks for injecting into other functions if-none or if-singular (rather than just returning the preformatted function). It needed an "on or off" var with a per-switch customized label(s) for platform settings. It also needed a way for $ip to be dynamic depending on request so that it would take form of forwarded_for.
Also i didnt see anyone address isset() vs !empty() -- its possible to enter nothing for x_forwarded_for yet still trigger isset() truth resulting in blank var, a way to get around is to use && and combine both as conditions. Keep in mind you can spoof words like "PWNED" as x_forwarded_for so make sure you sterilize to a real ip syntax if your outputting somewhere protected or into DB.
Also also, you can test using google translate if you need a multi-proxy to see the array in x_forwarder_for. If you wanna spoof headers to test, check this out Chrome Client Header Spoof extension. This will default to just standard remote_addr while behind anon proxy.
I dunno any case where remote_addr could be empty, but its there as fallback just in case.
// proxybuster - attempts to un-hide originating IP if [reverse]proxy provides methods to do so
$enableProxyBust = true;
if (($enableProxyBust == true) && (isset($_SERVER['REMOTE_ADDR'])) && (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) && (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))) {
$ip = end(array_values(array_filter(explode(',',$_SERVER['HTTP_X_FORWARDED_FOR']))));
$ipProxy = $_SERVER['REMOTE_ADDR'];
$ipProxy_label = ' behind proxy ';
} elseif (($enableProxyBust == true) && (isset($_SERVER['REMOTE_ADDR']))) {
$ip = $_SERVER['REMOTE_ADDR'];
$ipProxy = '';
$ipProxy_label = ' no proxy ';
} elseif (($enableProxyBust == false) && (isset($_SERVER['REMOTE_ADDR']))) {
$ip = $_SERVER['REMOTE_ADDR'];
$ipProxy = '';
$ipProxy_label = '';
} else {
$ip = '';
$ipProxy = '';
$ipProxy_label = '';
}
To make these dynamic for use in function(s) or query/echo/views below, say for log gen or error reporting, use globals or just echo em in wherever you desire without making a ton of other conditions or static-schema-output functions.
function fooNow() {
global $ip, $ipProxy, $ipProxy_label;
// begin this actions such as log, error, query, or report
}
Thank you for all your great thoughts. Please let me know if this could be better, still kinda new to these headers :)
I came up with this function that does not simply return the IP address but an array with IP information.
// Example usage:
$info = ip_info();
if ( $info->proxy ) {
echo 'Your IP is ' . $info->ip;
} else {
echo 'Your IP is ' . $info->ip . ' and your proxy is ' . $info->proxy_ip;
}
Here's the function:
/**
* Retrieves the best guess of the client's actual IP address.
* Takes into account numerous HTTP proxy headers due to variations
* in how different ISPs handle IP addresses in headers between hops.
*
* #since 1.1.3
*
* #return object {
* IP Address details
*
* string $ip The users IP address (might be spoofed, if $proxy is true)
* bool $proxy True, if a proxy was detected
* string $proxy_id The proxy-server IP address
* }
*/
function ip_info() {
$result = (object) array(
'ip' => $_SERVER['REMOTE_ADDR'],
'proxy' => false,
'proxy_ip' => '',
);
/*
* This code tries to bypass a proxy and get the actual IP address of
* the visitor behind the proxy.
* Warning: These values might be spoofed!
*/
$ip_fields = array(
'HTTP_CLIENT_IP',
'HTTP_X_FORWARDED_FOR',
'HTTP_X_FORWARDED',
'HTTP_X_CLUSTER_CLIENT_IP',
'HTTP_FORWARDED_FOR',
'HTTP_FORWARDED',
'REMOTE_ADDR',
);
foreach ( $ip_fields as $key ) {
if ( array_key_exists( $key, $_SERVER ) === true ) {
foreach ( explode( ',', $_SERVER[$key] ) as $ip ) {
$ip = trim( $ip );
if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) !== false ) {
$forwarded = $ip;
break 2;
}
}
}
}
// If we found a different IP address then REMOTE_ADDR then it's a proxy!
if ( $forwarded != $result->ip ) {
$result->proxy = true;
$result->proxy_ip = $result->ip;
$result->ip = $forwarded;
}
return $result;
}
As someone said previously, the key here is for what reason you want to store user's ips.
I'll give an example from a registration system I work on and of course the solution just to contribute sth in this old discussion that comes frequently in my searches.
Many php registration libraries use ip to throttle/lock out failed attempts based on user's ip.
Consider this table:
-- mysql
DROP TABLE IF EXISTS `attempts`;
CREATE TABLE `attempts` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ip` varchar(39) NOT NULL, /*<<=====*/
`expiredate` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- sqlite
...
Then, when a user tries to do a login or anything related with servicing like a password reset, a function is called at the start:
public function isBlocked() {
/*
* used one of the above methods to capture user's ip!!!
*/
$ip = $this->ip;
// delete attempts from this ip with 'expiredate' in the past
$this->deleteAttempts($ip, false);
$query = $this->dbh->prepare("SELECT count(*) FROM {$this->token->get('table_attempts')} WHERE ip = ?");
$query->execute(array($ip));
$attempts = $query->fetchColumn();
if ($attempts < intval($this->token->get('attempts_before_verify'))) {
return "allow";
}
if ($attempts < intval($this->token->get('attempts_before_ban'))) {
return "captcha";
}
return "block";
}
Say, for example, $this->token->get('attempts_before_ban') === 10 and 2 users come for the same ips as is the case in the previous codes where headers can be spoofed, then after 5 attempts each both are banned!
Even worst, if all come from the same proxy then only the first 10 users will be logged and all the rest will be banned!
The critical here is that we need a unique index on table attempts and we can get it from a combination like:
`ip` varchar(39) NOT NULL,
`jwt_load varchar(100) NOT NULL
where jwt_load comes from a http cookie that follows the json web token technology where we store only the encrypted payload that should contain an arbitrary/unique value for every user.
Of course the request should be modified to: "SELECT count(*) FROM {$this->token->get('table_attempts')} WHERE ip = ? AND jwt_load = ?" and the class should also initiate a private $jwt.
I do wonder if perhaps you should iterate over the exploded HTTP_X_FORWARDED_FOR in reverse order, since my experience has been that the user's IP address ends up at the end of the comma-separated list, so starting at the start of the header, you're more likely to get the ip address of one of the proxies returned, which could potentially still allow session hijacking as many users may come through that proxy.
From Symfony's Request class
https://github.com/symfony/symfony/blob/1bd125ec4a01220878b3dbc3ec3156b073996af9/src/Symfony/Component/HttpFoundation/Request.php
const HEADER_FORWARDED = 'forwarded';
const HEADER_CLIENT_IP = 'client_ip';
const HEADER_CLIENT_HOST = 'client_host';
const HEADER_CLIENT_PROTO = 'client_proto';
const HEADER_CLIENT_PORT = 'client_port';
/**
* Names for headers that can be trusted when
* using trusted proxies.
*
* The FORWARDED header is the standard as of rfc7239.
*
* The other headers are non-standard, but widely used
* by popular reverse proxies (like Apache mod_proxy or Amazon EC2).
*/
protected static $trustedHeaders = array(
self::HEADER_FORWARDED => 'FORWARDED',
self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR',
self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST',
self::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO',
self::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT',
);
/**
* Returns the client IP addresses.
*
* In the returned array the most trusted IP address is first, and the
* least trusted one last. The "real" client IP address is the last one,
* but this is also the least trusted one. Trusted proxies are stripped.
*
* Use this method carefully; you should use getClientIp() instead.
*
* #return array The client IP addresses
*
* #see getClientIp()
*/
public function getClientIps()
{
$clientIps = array();
$ip = $this->server->get('REMOTE_ADDR');
if (!$this->isFromTrustedProxy()) {
return array($ip);
}
if (self::$trustedHeaders[self::HEADER_FORWARDED] && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED])) {
$forwardedHeader = $this->headers->get(self::$trustedHeaders[self::HEADER_FORWARDED]);
preg_match_all('{(for)=("?\[?)([a-z0-9\.:_\-/]*)}', $forwardedHeader, $matches);
$clientIps = $matches[3];
} elseif (self::$trustedHeaders[self::HEADER_CLIENT_IP] && $this->headers->has(self::$trustedHeaders[self::HEADER_CLIENT_IP])) {
$clientIps = array_map('trim', explode(',', $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_IP])));
}
$clientIps[] = $ip; // Complete the IP chain with the IP the request actually came from
$firstTrustedIp = null;
foreach ($clientIps as $key => $clientIp) {
// Remove port (unfortunately, it does happen)
if (preg_match('{((?:\d+\.){3}\d+)\:\d+}', $clientIp, $match)) {
$clientIps[$key] = $clientIp = $match[1];
}
if (!filter_var($clientIp, FILTER_VALIDATE_IP)) {
unset($clientIps[$key]);
}
if (IpUtils::checkIp($clientIp, self::$trustedProxies)) {
unset($clientIps[$key]);
// Fallback to this when the client IP falls into the range of trusted proxies
if (null === $firstTrustedIp) {
$firstTrustedIp = $clientIp;
}
}
}
// Now the IP chain contains only untrusted proxies and the client IP
return $clientIps ? array_reverse($clientIps) : array($firstTrustedIp);
}
I'm surprised no one has mentioned filter_input, so here is Alix Axel's answer condensed to one-line:
function get_ip_address(&$keys = ['HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'HTTP_CLIENT_IP', 'REMOTE_ADDR'])
{
return empty($keys) || ($ip = filter_input(INPUT_SERVER, array_pop($keys), FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE))? $ip : get_ip_address($keys);
}
I know this is too late to answer. But you may try these options:
Option 1: (Using curl)
$ch = curl_init();
// set URL and other appropriate options
curl_setopt($ch, CURLOPT_URL, "https://ifconfig.me/");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// grab URL and pass it to the browser
$ip = curl_exec($ch);
// close cURL resource, and free up system resources
curl_close($ch);
return $ip;
Option 2: (Works good on mac)
return trim(shell_exec("dig +short myip.opendns.com #resolver1.opendns.com"));
Option 3: (Just used a trick)
return str_replace('Current IP CheckCurrent IP Address: ', '', strip_tags(file_get_contents('http://checkip.dyndns.com')));
Might be a reference:
https://www.tecmint.com/find-linux-server-public-ip-address/
Though this post very old but the topic still demands attention. So here I come with another solution I used in my project. I found other solutions here either incomplete or too complex to understand.
if (! function_exists('get_visitor_IP'))
{
/**
* Get the real IP address from visitors proxy. e.g. Cloudflare
*
* #return string IP
*/
function get_visitor_IP()
{
// Get real visitor IP behind CloudFlare network
if (isset($_SERVER["HTTP_CF_CONNECTING_IP"])) {
$_SERVER['REMOTE_ADDR'] = $_SERVER["HTTP_CF_CONNECTING_IP"];
$_SERVER['HTTP_CLIENT_IP'] = $_SERVER["HTTP_CF_CONNECTING_IP"];
}
// Sometimes the `HTTP_CLIENT_IP` can be used by proxy servers
$ip = #$_SERVER['HTTP_CLIENT_IP'];
if (filter_var($ip, FILTER_VALIDATE_IP)) {
return $ip;
}
// Sometimes the `HTTP_X_FORWARDED_FOR` can contain more than IPs
$forward_ips = #$_SERVER['HTTP_X_FORWARDED_FOR'];
if ($forward_ips) {
$all_ips = explode(',', $forward_ips);
foreach ($all_ips as $ip) {
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)){
return $ip;
}
}
}
return $_SERVER['REMOTE_ADDR'];
}
}
You pretty much answered your own question! :)
function getRealIpAddr() {
if(!empty($_SERVER['HTTP_CLIENT_IP'])) //Check IP address from shared Internet
{
$IPaddress = $_SERVER['HTTP_CLIENT_IP'];
}
elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) //To check IP address is passed from the proxy
{
$IPaddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
}
else
{
$IPaddress = $_SERVER['REMOTE_ADDR'];
}
return $IPaddress;
}
Source
Just a VB.NET version of the answer:
Private Function GetRequestIpAddress() As IPAddress
Dim serverVariables = HttpContext.Current.Request.ServerVariables
Dim headersKeysToCheck = {"HTTP_CLIENT_IP", _
"HTTP_X_FORWARDED_FOR", _
"HTTP_X_FORWARDED", _
"HTTP_X_CLUSTER_CLIENT_IP", _
"HTTP_FORWARDED_FOR", _
"HTTP_FORWARDED", _
"REMOTE_ADDR"}
For Each thisHeaderKey In headersKeysToCheck
Dim thisValue = serverVariables.Item(thisHeaderKey)
If thisValue IsNot Nothing Then
Dim validAddress As IPAddress = Nothing
If IPAddress.TryParse(thisValue, validAddress) Then
Return validAddress
End If
End If
Next
Return Nothing
End Function
/**
* Sanitizes IPv4 address according to Ilia Alshanetsky's book
* "php|architect?s Guide to PHP Security", chapter 2, page 67.
*
* #param string $ip An IPv4 address
*/
public static function sanitizeIpAddress($ip = '')
{
if ($ip == '')
{
$rtnStr = '0.0.0.0';
}
else
{
$rtnStr = long2ip(ip2long($ip));
}
return $rtnStr;
}
//---------------------------------------------------
/**
* Returns the sanitized HTTP_X_FORWARDED_FOR server variable.
*
*/
public static function getXForwardedFor()
{
if (isset($_SERVER['HTTP_X_FORWARDED_FOR']))
{
$rtnStr = $_SERVER['HTTP_X_FORWARDED_FOR'];
}
elseif (isset($HTTP_SERVER_VARS['HTTP_X_FORWARDED_FOR']))
{
$rtnStr = $HTTP_SERVER_VARS['HTTP_X_FORWARDED_FOR'];
}
elseif (getenv('HTTP_X_FORWARDED_FOR'))
{
$rtnStr = getenv('HTTP_X_FORWARDED_FOR');
}
else
{
$rtnStr = '';
}
// Sanitize IPv4 address (Ilia Alshanetsky):
if ($rtnStr != '')
{
$rtnStr = explode(', ', $rtnStr);
$rtnStr = self::sanitizeIpAddress($rtnStr[0]);
}
return $rtnStr;
}
//---------------------------------------------------
/**
* Returns the sanitized REMOTE_ADDR server variable.
*
*/
public static function getRemoteAddr()
{
if (isset($_SERVER['REMOTE_ADDR']))
{
$rtnStr = $_SERVER['REMOTE_ADDR'];
}
elseif (isset($HTTP_SERVER_VARS['REMOTE_ADDR']))
{
$rtnStr = $HTTP_SERVER_VARS['REMOTE_ADDR'];
}
elseif (getenv('REMOTE_ADDR'))
{
$rtnStr = getenv('REMOTE_ADDR');
}
else
{
$rtnStr = '';
}
// Sanitize IPv4 address (Ilia Alshanetsky):
if ($rtnStr != '')
{
$rtnStr = explode(', ', $rtnStr);
$rtnStr = self::sanitizeIpAddress($rtnStr[0]);
}
return $rtnStr;
}
//---------------------------------------------------
/**
* Returns the sanitized remote user and proxy IP addresses.
*
*/
public static function getIpAndProxy()
{
$xForwarded = self::getXForwardedFor();
$remoteAddr = self::getRemoteAddr();
if ($xForwarded != '')
{
$ip = $xForwarded;
$proxy = $remoteAddr;
}
else
{
$ip = $remoteAddr;
$proxy = '';
}
return array($ip, $proxy);
}

PHP: file_get_contents is too slow

I'm trying to use ip-api.com/php
but there's a very slow response on my server and I figured out that it is because of the file_get_contents
So basically, I have a pretty simple script (got from github I think)
function get_ip() {
//Just get the headers if we can or else use the SERVER global
if ( function_exists( 'apache_request_headers' ) ) {
$headers = apache_request_headers();
} else {
$headers = $_SERVER;
}
//Get the forwarded IP if it exists
if ( array_key_exists( 'X-Forwarded-For', $headers ) && filter_var( $headers['X-Forwarded-For'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) ) {
$the_ip = $headers['X-Forwarded-For'];
} elseif ( array_key_exists( 'HTTP_X_FORWARDED_FOR', $headers ) && filter_var( $headers['HTTP_X_FORWARDED_FOR'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 )
) {
$the_ip = $headers['HTTP_X_FORWARDED_FOR'];
} else {
$the_ip = filter_var( $_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 );
}
return $the_ip;
}
$ip=get_ip();
$query = #unserialize(file_get_contents('http://ip-api.com/php/'.$ip));
When it comes to
$query = #unserialize(file_get_contents('http://ip-api.com/php/'.$ip));
It freezes for around 1min.
If you read their API you will see this:
Deprecated Use JSON. Almost all PHP sites now support json_decode(),
and it's faster than unserialize()
The reference is here
There you will also find how to do it with Json with an example you can leverage to get your point:
To receive the response in JSON format, send a GET request to
http://ip-api.com/json
You can supply an IP address or domain to lookup, or none to use your
current IP address.
The reference is here
In my experience, http://ip-api.com/json didn't respond to the server and it takes much time. When I call from localhost it working fine.
Now I am using https://freegeoip.app/json/ and it's allowed up to 15,000 queries per hour by default.

Find Available IP to host port

Im having a headache trying to figure out how to find an IP address in my list with the available ports.
My problem is quite simple actually but I just can't figure out how to program it.
I was hopping someone can point me in the right direction.
Maybe there is a way to optimize the list for it to be easier to search in it.
Any how here is my problem.
I have a list of IP and multiple ports on each of them own by a client.
I can have multiple clients on the same IP, but i can't have a client on multiple IP
So im trying to build a function that would tell me "hey you need to switch this client on this ip so you can have x client on this ip
So here is my example list.
$ReservedPortsOnIPs = array(
'10.0.0.1'=>array(
'TCP:80'=>array(
'ClientId'=>1
),
'TCP:443'=>array(
'ClientId'=>1
)
),
'10.0.0.2'=>array(
'TCP:80'=>array(
'ClientId'=>2
)
),
'10.0.0.3'=>array(
'TCP:443'=>array(
'ClientId'=>4
),
'TCP:22'=>array(
'ClientId'=>4
),
)
);
Now here is my code I have til now. ( im only able to find available IP ) so at the moment if for example im trying to find an ip available with lets say TCP:80 and TCP:443
There is none directly available but if I would change cleint 4 from 10.0.0.3 to 10.0.0.2 I would have 10.0.0.3 avaible for the ports im asking.
Im at a point where I need to do the same task but recursively but I have no idea how start ? :S
function PortAvailableInNetwork($Port, $ReservedPortsOnIPs){
foreach($ReservedPortsOnIPs as $IP => $Ports) {
if(!isset($Ports[$Port]))
return true; //We found it not used!
}
return false; // foreach runned and no ip has the port available.
}
function AddPort($Ports,$ClientId,&$ReservedPortsOnIPs)
{
//check if ports possibly available
if(!is_array($Ports))
{
if(!PortAvailableInNetwork($Ports,$ReservedPortsOnIPs))
return false;
}
else
foreach($Ports as $Port)
if(!PortAvailableInNetwork($Port,$ReservedPortsOnIPs))
return false;
$TmpReservedPortOnIp = $ReservedPortsOnIPs;
//Find Current Ports ClientId Has
$ClientsPorts = array();
if(!is_array($Ports))
$ClientsPorts[] = $Ports;
else
foreach($Ports as $Port)
$ClientsPorts[] = $Port;
foreach($ReservedPortsOnIPs as $IP => $Ports){
foreach($Ports as $Port => $PortInfo){
if($PortInfo['ClientId'] == $ClientId)
{
$ClientsPorts[] = $Port;
unset($TmpReservedPortOnIp[$IP][$Port]); //Remove the clientPort for search purposes
}
}
}
print_r($ClientsPorts);
//Now that we have all client ports lets search for an available ip address
$PortsAvailability = array();
foreach($TmpReservedPortOnIp as $IP => $Ports){
$PortsAvailability[$IP] = array('Available'=>array(),'UnAvailable'=>array());
foreach($ClientsPorts as $Port){
if(!isset($Ports[$Port]))
$PortsAvailability[$IP]['Available'][]=$Port;
else
$PortsAvailability[$IP]['UnAvailable'][]=$Port;
}
if(count($PortsAvailability[$IP]['Available']) == count($ClientsPorts))
return $IP;
}
//if we are at this step this mean no ip had all ports
usort($PortsAvailability,function($a,$b){
if(count($a['Available']) > count($b['Available']))
return -1;
else
return 1;
});
print_r($PortsAvailability);
}
echo AddPort(array('TCP:80','TCP:443'),5,$ReservedPortsOnIPs);
I am open to change my design, as I did this on top of my head the way I could see the problem and its maybe not the right way to do it.
thanks for your time reading my question :)

Prevent PHP script from being flooded

I want to prevent my script, from being flooded - if user hit F5 it is executing the script every time.
I want to prevent from this and allow one script execution per 2 seconds, is there any solution for that?
You can use memcache to do this ..
Simple Demo Script
$memcache = new Memcache ();
$memcache->connect ( 'localhost', 11211 );
$runtime = $memcache->get ( 'floodControl' );
if ((time () - $runtime) < 2) {
die ( "Die! Die! Die!" );
}
else {
echo "Welcome";
$memcache->set ( "floodControl", time () );
}
This is just a sample code .. there are also other thing to consider such as
A. Better IP address detection (Proxy , Tor )
B. Current Action
C. Maximum execution per min etc ...
D. Ban User after max flood etc
EDIT 1 - Improved Version
Usage
$flood = new FloodDetection();
$flood->check();
echo "Welcome" ;
Class
class FloodDetection {
const HOST = "localhost";
const PORT = 11211;
private $memcache;
private $ipAddress;
private $timeLimitUser = array (
"DEFAULT" => 2,
"CHAT" => 3,
"LOGIN" => 4
);
private $timeLimitProcess = array (
"DEFAULT" => 0.1,
"CHAT" => 1.5,
"LOGIN" => 0.1
);
function __construct() {
$this->memcache = new Memcache ();
$this->memcache->connect ( self::HOST, self::PORT );
}
function addUserlimit($key, $time) {
$this->timeLimitUser [$key] = $time;
}
function addProcesslimit($key, $time) {
$this->timeLimitProcess [$key] = $time;
}
public function quickIP() {
return (empty ( $_SERVER ['HTTP_CLIENT_IP'] ) ? (empty ( $_SERVER ['HTTP_X_FORWARDED_FOR'] ) ? $_SERVER ['REMOTE_ADDR'] : $_SERVER ['HTTP_X_FORWARDED_FOR']) : $_SERVER ['HTTP_CLIENT_IP']);
}
public function check($action = "DEFAULT") {
$ip = $this->quickIP ();
$ipKey = "flood" . $action . sha1 ( $ip );
$runtime = $this->memcache->get ( 'floodControl' );
$iptime = $this->memcache->get ( $ipKey );
$limitUser = isset ( $this->timeLimitUser [$action] ) ? $this->timeLimitUser [$action] : $this->timeLimitUser ['DEFAULT'];
$limitProcess = isset ( $this->timeLimitProcess [$action] ) ? $this->timeLimitProcess [$action] : $this->timeLimitProcess ['DEFAULT'];
if ((microtime ( true ) - $iptime) < $limitUser) {
print ("Die! Die! Die! $ip") ;
exit ();
}
// Limit All request
if ((microtime ( true ) - $runtime) < $limitProcess) {
print ("All of you Die! Die! Die! $ip") ;
exit ();
}
$this->memcache->set ( "floodControl", microtime ( true ) );
$this->memcache->set ( $ipKey, microtime ( true ) );
}
}
Store the last execution time of your script in a database or a
file.
Read from that file/database and compare to the current time.
If the difference is under 2 seconds, terminate the script.
Else, continue normally.
you can either use cookies (which can be disabled) so not a very good idea, or you can use store his ip address in the database, so if more then X tries from the same IP address then do not execute the code, just an if else statement, you will need a table with ip addresses time of request, number of tries
IF you do not want to use databases then you can use the following code
$file = "file.txt";
$file_content = file_get_contents($file);
$fh = fopen($file, 'w') or die("could not open file");
$now = time();
if($now - $file_content > 60){
// your code here
fwrite($fh, $now);
}else{
echo "Try again later";
}
fclose($fh);
but in this case, it won't be for each visitor but rather for all of them (so say user A came and execute the script, user B won't be able to execute it until 60 seconds pass.
The best way would be to store the time on serverside. If you leave the information on client side it would be easy to by pass.
I would for example save the timestamp in a table. That inputs and checks against spamming your script. And would be easy to set tolerence.
use either apc cache or mencache to store information storing to database or reading from file i believe is time/resource consuming

IP Address using PHP not reliable

I use a javascript API from (http://www.iplocationtools.com) to give me the location data from a visitors IP. For some reason, their API won't give me the actual IP of the visitor, just the other info, which is why I have to use PHP and CodeIgniter to give me the IP instead.
So I use CodeIgniter/PHP to get the IP of a visitor and add it to the database along with location data from above by using PHP's ip2long([the ip code igniter gives me])
I'm confused when my database table looks like this: http://pulse.media.mit.edu/images/1.png
Which is wrong? I'm tempted to believe CodeIgniter is wrong since it gives me the same IP so many times. Age and sex are self reported and I doubt one person is making up all this information.
At the end of the day, all we really need is the users IP and location, preferably from the same source, so we don't compound errors.
Anybody have a better idea on how to do this?
EDIT: Here is the code that I'm using to get the IP address from CodeIgniter
$data['ip_address'] = ip2long($this->input->ip_address());
$this->pulse_model->voter_info($data);
Then the voter_info function just inserts it into the database where it's stored as an INT(11).
And here is the function ip_address:
function ip_address()
{
if ($this->ip_address !== FALSE)
{
return $this->ip_address;
}
if (config_item('proxy_ips') != '' && $this->server('HTTP_X_FORWARDED_FOR') && $this->server('REMOTE_ADDR'))
{
$proxies = preg_split('/[\s,]/', config_item('proxy_ips'), -1, PREG_SPLIT_NO_EMPTY);
$proxies = is_array($proxies) ? $proxies : array($proxies);
$this->ip_address = in_array($_SERVER['REMOTE_ADDR'], $proxies) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR'];
}
elseif ($this->server('REMOTE_ADDR') AND $this->server('HTTP_CLIENT_IP'))
{
$this->ip_address = $_SERVER['HTTP_CLIENT_IP'];
}
elseif ($this->server('REMOTE_ADDR'))
{
$this->ip_address = $_SERVER['REMOTE_ADDR'];
}
elseif ($this->server('HTTP_CLIENT_IP'))
{
$this->ip_address = $_SERVER['HTTP_CLIENT_IP'];
}
elseif ($this->server('HTTP_X_FORWARDED_FOR'))
{
$this->ip_address = $_SERVER['HTTP_X_FORWARDED_FOR'];
}
if ($this->ip_address === FALSE)
{
$this->ip_address = '0.0.0.0';
return $this->ip_address;
}
if (strpos($this->ip_address, ',') !== FALSE)
{
$x = explode(',', $this->ip_address);
$this->ip_address = trim(end($x));
}
if ( ! $this->valid_ip($this->ip_address))
{
$this->ip_address = '0.0.0.0';
}
return $this->ip_address;
}
$_SERVER['REMOTE_ADDR'] is the PHP code to return the IP address of the person viewing the page.
Old versions of ip2long() will return -1 if the IPv4 address is invalid. You may want to use inet_pton instead and expand the field used to hold it to 128 bits.
Based on your code, it looks like $this->input->ip_address() has the possibility of returning '0.0.0.0' if the IP is not valid or could not be determined. However, your comments also state that you need to record the ip address even if the above method returns '0.0.0.0'.
First, I'd recommend checking to see if $this->input->ip_address() and $this->valid_ip() are working as expected. Is $this->valid_ip() returning false for IP's that should be considered valid?
Second, I'd update your code to always fall back to $_SERVER['REMOTE_ADDR'] if $this->input->ip_address() returns '0.0.0.0'.
$ip_address = $this->input->ip_address();
if($ip_address == '0.0.0.0') {
$ip_address = $_SERVER['REMOTE_ADDR'];
}
$data['ip_address'] = ip2long($ip_address);
$this->pulse_model->voter_info($data);
Or if you wanted, you could not use $this->input->ip_address() and do as #rockerest suggests and just use $_SERVER['REMOTE_ADDR'] to being with.
$data['ip_address'] = ip2long($_SERVER['REMOTE_ADDR']);
$this->pulse_model->voter_info($data);

Categories