In PHP, it is very easy to detect an IP address at a basic level:
echo 'You know, your IP address is ' . $_SERVER['REMOTE_ADDR'] . '?';
Warning: Getting the IP address as the key of the
$_SERVER['REMOTE_ADDR']
field is only possible if PHP was called from a browser. In CLI mode (for example, running from Terminal with cron), the IP address is not available (this makes sense, since no network request is being made).
After many years of development, I finally stuck with this implementation:
function getIp(): string{if (isset($_SERVER['HTTP_CF_CONNECTING_IP'])) { // Cloudflare support$ip = $_SERVER['HTTP_CF_CONNECTING_IP'];} elseif (isset($_SERVER['REMOTE_ADDR']) === true) {$ip = $_SERVER['REMOTE_ADDR'];if (preg_match('/^(?:127|10)\.0\.0\.[12]?\d{1,2}$/', $ip)) {if (isset($_SERVER['HTTP_X_REAL_IP']) {$ip = $_SERVER['HTTP_X_REAL_IP'];} elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];}}} else {$ip = '127.0.0.1';}if (in_array($ip, ['::1', '0.0.0.0', 'localhost'], true) {$ip = '127.0.0.1';}$filter = filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);if ($filter === false) {$ip = '127.0.0.1';}return $ip;}
Much better then:
echo 'Do you know that your IP address is ' . getIp() . '?';
If the IP can be found out directly, or is IPv6 only, or is CLI mode (for example, cron), 127.0.0.1
(localhost) is returned.
Implementations that take into account the X-Forwarded-For
and X-Real-IP
headers are extremely dangerous directly in PHP, because data can easily be modified and an attacker can spoof a fake IP address to, for example, view the administration or activate the Debug mode of the site (Nette Tracy). On the other hand, we have to accept some proxied requests (for example, when proxing traffic through Cloudflare, or when running Apache and Ngnix on the same machine, when they are called locally right after each other).
In the case of direct user access to the server, there is only one correct solution, and that is to ensure on Apache (via the RemoteIP
extension) and in Nginx via the remote_ip
extension that X-Forwarded-For
is set from the actual IP address of the visitor, and that the IP address cannot be set with an HTTP header.
The $_SERVER['REMOTE_ADDR']
field automatically gets the correct IP address (that is, the IP address from which the request came directly to PHP) and we don't have to deal with it.
It often happens that a user accesses through a proxy. Then the actual IP address is stored in the variable $_SERVER['HTTP_X_FORWARDED_FOR']
.
This case can occur, for example, when routing on the server is resolved using the Ngnix -> Apache -> PHP
method, where Ngnix
serves as a reverse proxy before Apache
. In this case, PHP sees only the IP address within the internal network (usually of the form 127.0.0.*
).
For example, the Cloudflare service may behave in this way, and care should be taken whether we are working with the IP address of the actual user or the proxy. For me, the best way is to use the getIp()
function mentioned at the beginning of this article. We can ensure Cloudflare detection by verifying the existence of the $_SERVER['HTTP_CF_CONNECTING_IP']
key, which is automatically transmitted in every proxied request.
It depends on what IP address you have available.
ip2long
function is used for this,If your database server does not directly support a data type for the IP address, I recommend storing the IP address as varchar(39)
, where both versions will fit as a string and be human readable.
When storing the IP address, consider whether it makes sense to also store the domain name detected by the
gethostbyaddr
function. When listing and searching, you can't find out the names because it takes a very long time and they can change over time.
The ideal solution is to create a list of blocked IP addresses and compare this list with the current IP address on each request. If the addresses match, the request will be stopped immediately.
$blackList = ['first-ip','second-ip',];if (\in_array(getIp(), $blackList, true) === true) {echo 'Unfortunately you have a blocked ip address :-(';die; // End request}
The example assumes an implementation of getIp()
as in the example above.
A more powerful solution is to verify the occurrence of the index in the array:
$blackList = ['first-ip' => true,'second-ip' => true,];if (isset($blackList[getIp()]) === true) {echo 'Unfortunately you have a blocked ip address :-(';die; // End request}
The IP address of the server is usually stored in the $_SERVER['SERVER_ADDR']
field and its name can be obtained by the gethostbyaddr($_SERVER['SERVER_ADDR'])
construct.
However, if the Ngnix -> Apache -> PHP
concept is used and Ngnix
is in the role of a reverse proxy, the real IP address of the server is not displayed.
In this case, the server name can be found in the $_SERVER['SERVER_NAME']
field, or by using the php_uname('n')
function. Official uname function documentation.
We can then use this trick to find out the public IP address of the server: gethostbyname(php_uname('n'))
.
Jan Barášek Více o autorovi
Autor článku pracuje jako seniorní vývojář a software architekt v Praze. Navrhuje a spravuje velké webové aplikace, které znáte a používáte. Od roku 2009 nabral bohaté zkušenosti, které tímto webem předává dál.
Rád vám pomůžu:
Články píše Jan Barášek © 2009-2023 | Kontakt | Mapa webu
Status | Aktualizováno: ... | en