I have many projects with functionalities relying on IP addresses provided by $_SERVER['REMOTE_ADDR]
, $_SERVER['HTTP_X_FORWARDED_FOR']
, and $_SERVER['CLIENT_IP']
.
IPV4 addresses are easy to match since we always receive them in the same format: 4 integers without the leading 0s, separated by a dot .
.
Whereas IPV6 addresses can be compressed. Ex: FF01:0:0:0:0:0:0:101 -> FF01::101
I've been researching this issue but haven't found anything relevant, so I'm asking for your experience. Is $_SERVER['REMOTE_ADDR]
using a standard? Is it safe to assume that it will always be received as compressed or uncompressed?
Or should I compress all my IPV6 string before I try to try test them?
Note:
Ideally I would like to handle IPV6 addresses as strings rather than binary structure, to improve readability in Databases / Source code and allow easier IP range matching.
If you use inet_pton()
first, and then convert it back to a string with inet_ntop()
you should have a consistent string representation. I wouldn't rely on the input to be consistent...
The CGI spec is clear that any RFC compliant IPv6 address is valid:
4.1.8. REMOTE_ADDR
The REMOTE_ADDR variable MUST be set to the network address of the
client sending the request to the server.
REMOTE_ADDR = hostnumber
hostnumber = ipv4-address | ipv6-address
ipv4-address = 1*3digit "." 1*3digit "." 1*3digit "." 1*3digit
ipv6-address = hexpart [ ":" ipv4-address ]
hexpart = hexseq | ( [ hexseq ] "::" [ hexseq ] )
hexseq = 1*4hex *( ":" 1*4hex )
The format of an IPv6 address is described in RFC 3513 [15].
A sane programmer validates all inputs. Therefore you should treat any environment variable as tainted input and validate/transform. Headers provided by a client, such as X-Forward-For et al should ALWAYS be treated with suspicion.
So what about expanding IPv6 addresses?
This question has been asked before and there are several solutions including a PEAR one
Hope this helps!
The recommended format for IPv6 address is in RFC 5952.
However you cannot rely on all addresses being in that format, and the string format is particularly lousy for range comparisons. Reading the RFC will give you some idea as to just how many ways it's possible to legally represent the same IPv6 address.
You really should parse the address (using inet_pton
as mentioned elsewhere) and perform your range comparisons on the resulting 128 bit field.
More often than not you'll only care about the 64 most significant bits, which fits nicely into a long
on most architectures.