28

I am implementing subscription/response possibility using nodejs (express). When visitor send request, beside other parameters within request (port, time interval etc...) I am going to collect ip in order to be able to send response to that ip from time to time.

I am fetching visitor ip address using folowing:

  var ip = req.headers['x-forwarded-for'] || 
             req.connection.remoteAddress || 
             req.socket.remoteAddress ||
             req.connection.socket.remoteAddress;

based on How can I get the user's IP address using Node.js?

Point is that after i get ip I have something like this : "::ffff:192.168.1.10" (explained at request.connection.remoteAddress Now Prefixed in ::ffff in node.js )

I am wondering, is it "safe" just to strip "::ffff:" prefix in order to get ip address which I will be able to use in order to reply via http response, or I am missing something else over here, and that is not what i should do?

cool
  • 3,225
  • 3
  • 33
  • 58

8 Answers8

37

Yes, it's safe to strip. Here's a quick way to do it.

address.replace(/^.*:/, '')

What happens is your OS is listening with a hybrid IPv4-IPv6 socket, which converts any IPv4 address to IPv6, by embedding it within the IPv4-mapped IPv6 address format. This format just prefixes the IPv4 address with :ffff:, so you can recover the original IPv4 address by just stripping the :ffff:. (Some deprecated mappings prefix with :: instead of :ffff:, so we use the regex /^.*:/ to match both forms.)

If you aren't sure that incoming IPv6 address came from an IPv4, you can check that it matches the IPv6-mapping template first:

template = /^:(ffff)?:(?!0)(?!.*\.$)((1?\d?\d|25[0-5]|2[0-4]\d)(\.|$)){4}$/
has_ipv4_version = template.test(address)
  • 4
    This isn't remotely safe. What if you had a legitimate IPv6 address? – Brad Jul 04 '16 at 08:13
  • 1
    The example address `::ffff:192.168.1.10` *is* legitimate! The question isn't whether the IPv6 address is legitimate; it's whether you can get a legitimate IPv4 address by stripping off the `::ffff:`. And if your OS is automatically generating the IPv6 address from an IPv4 address, then the answer to that question is **yes**. – Michael Matthew Toomim Jul 05 '16 at 15:37
  • Let me rephrase... what if you had an IPv6 address that wasn't a mapped IPv4 address? Your regex will destroy it. – Brad Jul 05 '16 at 16:44
  • 1
    The OP specifically said that his/her IPv4 addresses are being remapped to IPv6 due to a hybrid dual-stack IPv6/IPv4 socket (as explained in the [link](http://stackoverflow.com/questions/24896386/request-connection-remoteaddress-now-prefixed-in-ffff-in-node-js) given). Thus there DOES exist a IPv4 address. In general, these [IPv4-mapped IPv6 addresses](https://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses) have a particular format that you can detect if you want a more general algorithm. I'll update my answer. – Michael Matthew Toomim Jul 05 '16 at 21:08
  • 4
    Indeed, my question was is this safe thing to do. Additionally @Brad have a point as well. After my research (specifically for my case) stripping was more then enough. I am marking this answer as accepted one having in mind that everything is included - Confirmation, is it safe or not, how to strip it and how to detect is it mapped IPv4 or not in order to avoid issues. – cool Aug 09 '16 at 15:04
30

If you want to get the ip address of IPv4, you can use:

http.createServer(callback).listen(port, '0.0.0.0');

then, you will get what you want

req.connection.remoteAddress   // 192.168.1.10

Here is the relevant document of nodejs

user7333128
  • 311
  • 3
  • 2
  • 1
    And this is the only valid answer in the whole topic. What a shame it is not most upvoted. Let me somehow GRAB ATTENTION TO THIS ANSWER. Do not ever parse IP with regexps and string functions! – polkovnikov.ph Dec 20 '17 at 17:58
  • 2
    I totally disagree. As I mentioned above, the way of picking up the address which will cover all of the cases will have "::ffff:" in front. That is the whole POINT of question. I never asked how can I fetch the IP, instead of that I asked is it safe to strip prefix which is added to it! Even further, above solution will not pick the remote address properly if server is behind the proxy! – cool Apr 27 '18 at 18:28
  • 3
    `req.connection.remoteAddress` gives me the same IP-6-extended address (from localhost) as described in the accepted answer: `::ffff:127.0.0.1`. So this answer does not seem to be true anymore. – trollkotze Aug 22 '18 at 12:50
2
app.post('/xyz',function(request,response)
{
    var IPFromRequest=request.connection.remoteAddress;
    var indexOfColon = IPFromRequest.lastIndexOf(':');
    var IP = IPFromRequest.substring(indexOfColon+1,IPFromRequest.length);
    console.log(IP);
});
2

I would split the remoteAddress ::ffff:192.168.1.10 by the : delimiter and simply take the value of the output array at index array.length - 1

like so:

const remoteAddress = '::ffff:192.168.0.3'
const array = remoteAddress.split(':')
const remoteIP = array[array.length - 1]
console.log(remoteIP)

prints out 192.168.0.3

ddaaggeett
  • 149
  • 2
  • 10
  • 1
    Question was not how to strip it ... (even selected answer covers that as well). It was mostly, "IS IT SAFE TO STRIP IT" and what kind of consequences we can have because of that. – cool Nov 14 '18 at 13:22
  • welp, that's what i get for answering to the wrong stack overflow tab. – ddaaggeett Nov 20 '18 at 03:58
1

This node code ...

returns ipv6 to ipv4 IF the ipv6 address is really a mapped ipv4 address else it returns a normalised ipv6 address else it just returns the ip string it originally had

var ip = (function (req) {
    var ipaddr = require('ipaddr.js');
    var ipString = (req.headers["X-Forwarded-For"] ||
        req.headers["x-forwarded-for"] ||
        '').split(',')[0] ||
        req.connection.remoteAddress;

    if (ipaddr.isValid(ipString)) {
        try {
            var addr = ipaddr.parse(ipString);
            if (ipaddr.IPv6.isValid(ipString) && addr.isIPv4MappedAddress()) {
                return addr.toIPv4Address().toString();
            }
            return addr.toNormalizedString();
        } catch (e) {
            return ipString;
        }
    }
    return 'unknown';
}(req));

https://www.npmjs.com/package/ipaddr.js

https://github.com/whitequark/ipaddr.js

danday74
  • 52,471
  • 49
  • 232
  • 283
  • based on third party script "ipaddr.js" ? Can you please provide a github link? – cool Dec 17 '15 at 21:29
  • 1
    links added hope that helps – danday74 Dec 18 '15 at 08:40
  • 1
    Caution... your code blindly trusts any upstream `X-Forwarded-For` headers. If you're using Express, you can use it's "trust proxy" feature to handle this for you. Otherwise, you must do it. Also, the headers are always lower-case so you don't need to check for both. – Brad Jul 04 '16 at 08:17
0

Just to add onto the answer provided by Michael Matthew Toomim,

If you want to test to see if the IP is an IPv4 address mapped as an IPv6, you will probably want to adjust the regex to this:

/^:{1,2}(ffff)?:(?!0)(?!.*\.$)((1?\d?\d|25[0-5]|2[0-4]\d)(\.|$)){4}$/

The difference being /^:{1,2} instead of /^:, which allows for both addresses which start with ::ffff: and :ffff:.

JakGuru
  • 101
  • 1
  • 8
-2

Had the same problem...im also new at javascript but i solved this with .slice

var ip = req.connection.remoteAddress;

if (ip.length < 15) 
{   
    ip = ip;
}
else
{
    var nyIP = ip.slice(7);
    ip = nyIP;
}
aCo
  • 1
-3

req.connection.remoteAddress.substring(7,req.connection.remoteAddress.length)

NewToPi
  • 187
  • 2
  • 9