We are on a cloud server with CF2018 Enterprise. We want to implement access restrictions to specific folders for certain user IP addresses and, of course, CGI variables such as REMOTE_ADDR are useless since we just get our cloud proxy address. CGI.REMOTE_ADDR has bit more promise as I am not sure it can be easily spoofed, but has anyone tackled such a problem and what technique did you use?
We also have CGI.X_FORWARDED_FOR and this does have the client's IP address, but again I am not sure as to its "spoofability". If we ensured that the X_FORWARDED_FOR was the IP we wanted and the REMOTE_ADDR was our proxy gateway address, would that be sufficient to allow/disallow users to certain folders to run .cfm scripts?
If you have a load balancer (whether it's an actual load balancer or a load balancing service like Amazon offers) or a proxy, and if the only way public network users can reach your servers is via the load balancer or proxy, then you can certainly rely on X-Forwarded-For to identify the client's IP address, implement client access rules, etc. You will not be able to use CGI.REMOTE_ADDR or anything else, because those will have values provided by the load balancer or proxy.
Dave Watts, Eidolon LLC
Oh, and this isn't really "security in the cloud" question - it would be the same if you had an on-premise load balancer or proxy set up the same way.
Dave Watts, Eidolon LLC
We are on Azure cloud, hence my phrasing, but yes, it's in essence a proxy. It was the spoofing that was the concern. So if we trust the load balancer and there is no other access to the server without the LB we should be ok.
We just installed the Firefox X-Forwarded-For spoof plugin and even though we can only get to our site one way, we can still spoof the header. It just goes to show that you must never trust the X-Forwarded-For even if your site is only available through a load balancer or proxy. In this case Azure picks up the client header and inserts it as the first in the X-Forwarded-For list, which in turn could be picked up in your code. Not good. This seems to be how Azure works - it just prepends the IP address to the existing list of IPs 😞
Update: We are using IIS with managed ARR and our hosting people inserted a URL rewrite rule to ensure that all Azure gateway IP ranges (10.0.0.1 to 10.0.0.15 in our case) were replaced with the real client IP address. This ensures CGI.REMOTE_ADDR has the clients real (original) IP address. CGI.X_FORWARDED_FOR was was a bit unreliable in this respect.
Just FYI: while you seem happy with your rewrite workaround, many reading along may not have that option.
For them, and you perhaps, consider instead the option of leveraging the fact that CF runs in Tomcat, which has an available "remoteipvalve", whose purpose is to solve this very problem of putting the ip found in a header like that into what populates the cgi.remote_addr in CF.
And it provides protections against the spoofing you're concerned with, in its available attributes that let you list additional characteristics to control trust in that ip header.
I did a blog post on the valve just last week, and I point to the tomcat docs for more on its additional attributes:
I will tweak it a bit at some point to add more clarity on this issue as well as another I was asked about privately since then.
Hi Charlie, that's a really good little nugget, unfortunately it did not work for us on Azure. I added the valve, restarted CF and the header did not exist. For us, as mentioned, we are behind ARR and the only way for us to get the "real" IP was to set up a rule to forward it from the Azure gateway IP (subnets) to our CF VMs. I'd rather your way worked since we don't then have to rely on the ISP. Any ideas why your way does not work for us?
When you say "the header does not exist", which do you mean? This reads as if you thought the valve creates header. It does not. It works BASED on whatever header you find IS passed in and want to be used to set the cgi.remote_addr.
As for the valve not working, perhaps the rewrite rule is hiding what is needed. Try having the host remove it, then try what I proposed just now in your other comment in the blog post just now.
Please clarify in both places once things are resolved.