gadicc / meteor-headers

Access HTTP headers on both server and client. Client IP with proxy support.
https://atmospherejs.com/gadicohen/headers
GNU Lesser General Public License v3.0
61 stars 21 forks source link

headers.methodClientIP(this) in Meteor.methods returns 127.0.0.1 even if no proxy is used #15

Closed MiroRadenovic closed 9 years ago

MiroRadenovic commented 10 years ago

Hello, I'm developing on my machine (ip: 172.16.223.175) a meteor application and I have added meteor-headers using mrt add headers

I have the following Meteor method:

Meteor.methods({
    //update methods
    insertComment : function(commentAttributes){
        console.log(headers.methodClientIP(this));
        console.log('-----');
        console.log(headers.get(this));
[ ...]

if I invoke the meteor Method from localhost:3000 my outputs is

0140305-13:04:46.046(1)? 127.0.0.1
I20140305-13:04:46.047(1)? -----
I20140305-13:04:46.047(1)? { host: 'localhost:3000',
I20140305-13:04:46.047(1)?   'user-agent': 'Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:26.0) Gecko/20100101 Firefox/26.0',
I20140305-13:04:46.047(1)?   accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
I20140305-13:04:46.047(1)?   'accept-language': 'en-US,en;q=0.5',
I20140305-13:04:46.048(1)?   'accept-encoding': 'gzip, deflate',
I20140305-13:04:46.049(1)?   cookie: '__utma=111872281.2106898642.1393501859.1393501859.1393521262.2; __utmz=111872281.1393501859.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); meteor_login_token=zgcsDcXKMkqHrLmTM',
I20140305-13:04:46.049(1)?   connection: 'keep-alive',
I20140305-13:04:46.049(1)?   'x-forwarded-for': '127.0.0.1',
I20140305-13:04:46.049(1)?   'x-forwarded-port': '3000',
I20140305-13:04:46.050(1)?   'x-forwarded-proto': 'http',
I20140305-13:04:46.051(1)?   'x-ip-chain': '127.0.0.1' }

.which is reasonable... but if I access the application locally from http://172.16.223.175:3000, I receive

0140305-13:06:24.122(1)? 127.0.0.1
I20140305-13:06:24.125(1)? -----
I20140305-13:06:24.128(1)? { host: '172.16.223.175:3000',
I20140305-13:06:24.128(1)?   'user-agent': 'Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:26.0) Gecko/20100101 Firefox/26.0',
I20140305-13:06:24.128(1)?   accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
I20140305-13:06:24.128(1)?   'accept-language': 'en-US,en;q=0.5',
I20140305-13:06:24.128(1)?   'accept-encoding': 'gzip, deflate',
I20140305-13:06:24.129(1)?   cookie: 'meteor_login_token=swTyRpYd3MDDpzGH2',
I20140305-13:06:24.129(1)?   connection: 'keep-alive',
I20140305-13:06:24.129(1)?   'x-forwarded-for': '172.16.223.175',
I20140305-13:06:24.130(1)?   'x-forwarded-port': '3000',
I20140305-13:06:24.134(1)?   'x-forwarded-proto': 'http',
I20140305-13:06:24.134(1)?   'x-ip-chain': '172.16.223.175,127.0.0.1' }

as you can see the headers.methodClientIP(this) returns 127.0.0.1

At the end, if I access the application from a different machine 172.16.223.135 i still receive the 127.0.0.1 using headers.methodClientIP(this):

0140305-13:09:23.578(1)? 127.0.0.1
I20140305-13:09:23.584(1)? -----
I20140305-13:09:23.587(1)? { host: '172.16.223.175:3000',
I20140305-13:09:23.590(1)?   connection: 'keep-alive',
I20140305-13:09:23.592(1)?   'cache-control': 'max-age=0',
I20140305-13:09:23.595(1)?   accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
I20140305-13:09:23.597(1)?   'user-agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36',
I20140305-13:09:23.599(1)?   referer: 'http://172.16.223.175:3000/fake',
I20140305-13:09:23.601(1)?   'accept-encoding': 'gzip,deflate,sdch',
I20140305-13:09:23.610(1)?   'accept-language': 'it-IT,it;q=0.8,en-US;q=0.6,en;q=0.4',
I20140305-13:09:23.611(1)?   cookie: '__utma=188728936.231755226.1393577617.1393577617.1393586661.2; __utmz=188728936.1393577617.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); meteor_login_token=qNPvK3TWjydBgCefx',
I20140305-13:09:23.611(1)?   'x-forwarded-for': '172.16.223.135',
I20140305-13:09:23.612(1)?   'x-forwarded-port': '3000',
I20140305-13:09:23.612(1)?   'x-forwarded-proto': 'http',
I20140305-13:09:23.623(1)?   'x-ip-chain': '172.16.223.135,127.0.0.1' }

The only way to get the correct ip, when accessing the application from the outside, even if don't use any proxy/load balancer is to use

headers.methodClientIP(this,1)

which actually return a correct value:

0140305-13:11:35.450(1)? 172.16.223.135
I20140305-13:11:35.459(1)? -----
I20140305-13:11:35.473(1)? { host: '172.16.223.175:3000',
I20140305-13:11:35.473(1)?   connection: 'keep-alive',
I20140305-13:11:35.479(1)?   'cache-control': 'max-age=0',
I20140305-13:11:35.479(1)?   accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
I20140305-13:11:35.480(1)?   'user-agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36',
I20140305-13:11:35.481(1)?   referer: 'http://172.16.223.175:3000/fake',
I20140305-13:11:35.481(1)?   'accept-encoding': 'gzip,deflate,sdch',
I20140305-13:11:35.483(1)?   'accept-language': 'it-IT,it;q=0.8,en-US;q=0.6,en;q=0.4',
I20140305-13:11:35.488(1)?   cookie: '__utma=188728936.231755226.1393577617.1393577617.1393586661.2; __utmz=188728936.1393577617.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); meteor_login_token=qNPvK3TWjydBgCefx',
I20140305-13:11:35.494(1)?   'x-forwarded-for': '172.16.223.135',
I20140305-13:11:35.495(1)?   'x-forwarded-port': '3000',
I20140305-13:11:35.499(1)?   'x-forwarded-proto': 'http',
I20140305-13:11:35.501(1)?   'x-ip-chain': '172.16.223.135,127.0.0.1' }

additional info: meteor version: 0.7.1.2 meteor-headers version: 0.0.15

thank you

MiroRadenovic commented 10 years ago

Hello I'm back! I have been looking at your code an I have found this at https://github.com/gadicohen/meteor-headers/blob/master/headers-server.js#L88

 headers.getClientIP = function(self, proxyCount) {
  checkSelf(self, 'getClientIP');
  var chain = this.get(self, 'x-ip-chain').split(',');
  if (typeof(proxyCount) == 'undefined')
    proxyCount = this.proxyCount;
  return chain[chain.length - proxyCount - 1];
}

basically the problem here is that i got my x-ip-chain starting always with 127.0.0.1. I have no idea on why 127.0.0.1 is always included, but why you did not use the header x-forwarded-for which actually is used for this scope?

taken from wikipedia (http://en.wikipedia.org/wiki/X-Forwarded-For) it says:

      The general format of the field is:
        X-Forwarded-For: client, proxy1, proxy2

Thank you!

gadicc commented 10 years ago

Hey. Meteor uses an internal proxy module, I don't recall the specifics unfortunately. Is this behaviour the same when your app is deployed as opposed to running it in development mode? I'd expect it to be the same, but I ask since in our deployment at Meteor.com, we don't see this behaviour: http://headers.meteor.com/

The reason we can't just use the first value in the list is because we can't trust it. If the user connects and sends his own X-Forward-For value, he could make the first address on the list whatever he wants. That's why we can only trust the value inserted by the last proxy we control/trust (which won't necessarily be the first on the list... it will be the last on the list before any proxies we trust, and our proxy can vouch that that is the IP that originated the connection).

MiroRadenovic commented 10 years ago

Hello and thank you for the reply. Related to the X-Forward-For i agree with you.

I'm still new in meteor and I don't know if something changed lately, but I see that everywhere I deploy the application, under x-ip-chan the 127.0.0.1 address is always there.

I have tried to run the my code :

on modulus my client headers are

{ host: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
  'cache-control': 'max-age=0',
  accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
  'user-agent': 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.117 Safari/537.36',
  'accept-encoding': 'gzip,deflate,sdch',
  'accept-language': 'it-IT,it;q=0.8,en-US;q=0.6,en;q=0.4',
  cookie: 'meteor_login_token=yvXKWWNAd7wWxPXm9',
  'x-forwarded-for': '93.33.104.233,127.0.0.1',
  connection: 'close',
  'x-forwarded-port': '25256',
  'x-forwarded-proto': 'http',
  'x-ip-chain': '93.33.104.233,127.0.0.1,107.21.216.112' }

and to make thing more clear:

I will send you a pull request, with my fix, so we can investigate it a little better :+1:

MiroRadenovic commented 10 years ago

there is: https://github.com/gadicohen/meteor-headers/pull/16

gadicc commented 10 years ago

Hey, I'm going to look into this issue more deeply to see if something changed in recent Meteor versions with respect to the internal proxy. However, you should be aware that x-ip-chain is generated by meteor-headers (it's not an actual header from the client or proxy), and is made up of x-forwarded-for and IP of the source of the connection to Meteor (a proxy, if one exists, or the client, if one doesn't. although now it seems one always does, and this might be a recent change).

To that end, taking the first value in x-ip-chain is exactly the same as taking the first value of x-forwarded-for. Your PR unfortunately creates a very dangerous situation where the client can pretend to be from any IP they like. You should just use a proxyCount of 1 for now, which is completely safe, and a lot easier than rewriting the code?

As I said, I will see if anything changed recently in Meteor that making this method behave incorrectly.

gadicc commented 10 years ago

There were some changes in Meteor 0.7.1, and I've made some related and unrelated changes to meteor-headers in commit https://github.com/gadicohen/meteor-headers/commit/a655ef825cc14238f3811f666fff46740bdeb7b1 (setProxyCount() is deprecated now in favour of HTTP_FORWARDED_COUNT environment varialbe).

Can you see how this affects the issues you described?

MiroRadenovic commented 10 years ago

ok, It took me while but i have test it. to me this now works fine with your update! browsing my meteor application on my local ip through http://172.16.223.181:3000 with

headers.methodClientIP(self);

it returns correctly 172.16.223.181

and through http://localhost:3000 it returns 127.0.0.1 to me this solves the issue and you could close it.

and last, but not less important: THANK YOU, THANK YOU and THANK YOU ;)

gadicc commented 9 years ago

Pleasure, glad you're up and working (belatedly) :)