Trusting X-Forwarded-For to "identify" a visitor - security

Session Hijacking
So I have a slight problem. I'm trying to identify a visitor, which is very hard if not impossible by $_SERVER veriables as mentioned in this question: Preventing session hijacking.
Possible Solution
To make a bit harder than just copying the cookie from Client A to Client B (which is sadly childsplay), I want to collect some info and validate this against something I have stored. In my database I want to store things like User-Agent, IP-Address, OS etc. This I will encrypt using MCRYPT and store. To match against a user, a lot of variables have to be set, this makes it somewhat harder than just copying the cookie contents to login.
The problem
Here's when my problem starts... The User-Agent and OS are nearly if not completely identical. The reason is that it are Fat Clients with the same bootable image. Another problem is the IP. The server in the Datacenter has a connection to the office. For our applications (even tho not externally accessible) the IP-Address is the same for every client. I found out that I could try to use the X-Forwarded-For header to distinguish IP addresses and thus make the user a bit more unique.
What's next?
What I would like to know is the following: How can I make sure the X-Forwarded-For is ALWAYS set without having to anything the clients have access to? Does something have to be added there by routing? Our connection is https, so I doubt I can just "inject" something. Next to that, if I can inject something like this, can the users client side do this?
The clients are in our internal office network and the applications (running in php) are not accessible from the outside

The X-Forwarded-For and User-Agent HTTP headers can easily be spoofed by any user (just as easily as copying a cookie from one machine to another).
Chrome extensions such as Header Hacker can be used on the client, and since your site is using HTTPS these headers cannot be added en route (as the headers need to added to the OSI application layer, not the transport layer).
If you're worried about users copying cookies between one another, is there any mechanism that would stop them sharing their username and password credentials? If you did manage to implement something that verified that their sessions remained on the same client machine, couldn't they simply work round it by logging in as each other?
Aside from my questions, for a solution you could introduce a local proxy into your internal network, purely for connecting to your site at the data centre. The site should reject any connections that are not from the IP of the proxy server (configure the web server or firewall to only accept the client IP of the proxy for web connections). Using this approach you will need to install an SSL certificate onto the proxy, which each client machine can trust. This will enable the proxy server to decrypt traffic, add the appropriate IP address header (overwriting any set by the client) and then forward it onto your server. The server code can then safely check the X-Forwarded-For header to make sure it remains constant per user session.
If this sounds like a good solution, please comment if you have any questions and I'll update my answer.
Two other thoughts:
You could use something to fingerprint the browser like panopticlick. However, as this is retrieving various values from the client and creating a fingerprint, it can all be spoofed if the headers are set the same as another user's. Also, as each machine is from the same bootable image, this might well be the same anyway.
Rolling session cookies: You could randomly regenerate the session using session_regenerate_id(). This will update the session ID of the client creating the request, and any other client using the same ID will then be logged out because they are sending the old session ID. Actually, you could do this on every request which will ensure that only the current client is using the current session.

Related

How to identify https clients through proxy connection

We have developed a corporate NodeJS application served through http/2 protocol and we need to identify clients by their IP address because the server need to send events to clients based on their IP (basically some data about phone calls).
I can successfully get client IP through req.connection.remoteAddress but there are some of the clients that can only reach the server through our proxy server.
I know about x-forwarded-for header, but this doesn't work for us because proxies can't modify http headers in ssl connections.
So i though I could get the IP from client side and send back to the server, for example, during the login process.
But, if I'm not wrong, browsers doesn't provide that information to javascript so we need a way to obtain that information first.
Researching about it, the only option I found out is obtaining from a server which could tell me the IP from where I'm reaching it.
Of course, through https I can't because of the proxy. But I can easily enable an http service just to serve the client IP.
But then I found out that browsers blocks http connections from https-served pages because of "mixed active content" issue.
I read about it and I found out that I can get "mixed passive content" and I succeed in downloading garbage data as image file through <img>, but when I try to do the same thing using an <object> element I get a "mixed active content" block issue again even in MDN documentation it says it's considered passive.
Is there any way to read that data either by that (broken) <img> tag or am I missing something to make the <object> element really passive?
Any other idea to achieve our goal will also be welcome.
Finally I found a solution:
As I said, I was able to perform an http request by putting an <img> tag.
What I was unable to do is to read downloaded data regardless if it were an actual image or not.
...but the fact is that the request was made and to which url is something that I can decide beforehand.
So all what I need to do is to generate a random key for each served login screen and:
Remember it in association with your session data.
Insert a, maybe hidden, <img> tag pointing to some http url containing that id.
As soon as your http server receive the request to download that image, you could read the real IP through the x-forwarded-for header (trusting your proxy, of course) and resolve to which active session it belongs.
Of course, you also must care to clear keys, regardless of being used or not, after a few time to avoid memory leak or even to be reused with malicious intentions.
FINAL NOTE: The only drawback of this approach is the risk that, some day, browsers could start blocking mixed passive content too by default.
For this reason I, in fact, opted by a double strategy approach. That is: additionally to the technique explained above, I also implemented an http redirector which does almost the same: It redirects all petitions to the root route ("/") to our https app. But it does so by a POST request containing a key which is previously associated to the client real IP.
This way, in case some day the first approach stops to work, users would be anyway able to access first through http. ...Which is in fact what we are going to do. But the first approach, while it continue working, could avoid problems if users decide to bookmark the page from within it (which will result in a bookmark to its https url).

in express using nodejs is it possible to prevent all curl requests that do not come from your hosted domain?

i have a registration end-point.
If someone discovered it, they could send garbage registrations into my database using cUrl.
Is it possible to prevent all cUrl requests that do not originate from www.mydomain.com so i dont need to worry about malicious account being created?
Note I'm using nginx on ubuntu and under /etc/nginx/sites-available/default i set
location /
{
#save origin ip address
proxy_set_header X-Forwarded-For $remote_addr;
#...
}
and in my end-point I have
app.get('/api/register',function(req,res)
{
var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
console.log(ip);
but the console always logs my host ip address, whether i send the request from my hosted website (using html and a form) or if i send a cURL request from my pc at home.
I also tried tinkering with
app.enable('trust proxy')
from Express.js: how to get remote client address
The registration API can be trivially discovered by looking at the source of your web client application or at the network traffic, so it should not be considered a secret.
The X-Forwarded-For header will provide the name / IP of any proxies that the query traversed on the way from the browser to your API, it will not provide an indication of where the form was loaded from (during your tests you get the IP of your server because that is where you have your nginx reverse proxy setup). The header that shows where your client code loaded from would be the Referrer header, something which is easily spoofed and not much of a security control. You could use a session to check that the API comes from a user that has previously loaded your code, but again this is easily reproducible outside your app.
So, to answer your question: no, there is no way to ensure that HTTP requests to your API only come from your client code. In a way it is the beauty of the API, so that clients can be implemented by anyone.
One approach to avoid the abuse of an unauthenticated API call such as the "registration" you are trying to protect would be to implement a CAPTCHA challenge whose solution is a parameter to your API call, with a complex enough CAPTCHA algorithm you ensure that the requests cannot be automated to create a large number of users, which is the threat you are trying to protect against.

What's the point of the X-Frame-Options header?

I work on an application where users can embed their website within surrounding content by loading it in an iframe. This obviously relies on the X-Frame-Options not being set on the users website to work. I was asked by a client to create a reverse proxy because they didn't want to remove the X-Frame-Options header from their site for security concerns.
I setup the proxy and everything works but what's the point of the X-Frame-Options header if its as simple as creating a proxy to circumvent?
I understand the header exists to prevent clickjacking but if anyone can just make a proxy to workaround it... does it really increase security?
I don't come from the enterprise dev world, can you help me understand the reasoning behind why the IT department would be resistant to removing the header?
I noticed google.com and facebook.com also set the header, so it can't be completely pointless can it?
Thanks
Any site served over http can have its content altered by using a proxy for example. So yes this is fairly pointless on http sites since it's so easily defeated.
Serving a site over https prevents this unless you have a proxy server which also intercepts https traffic. This is only possible by the proxy acting as a man-in-the-middle (MITM) so it decrypts the traffic at the proxy and then re-encrypts the traffic to send on to the server and same in way back. For this to work the proxy server either needs to know the server private key or, more likely, replaces the cert presented to its client with its own copy.
While MITM is usually associated with attacks there are some legitimate scenarios (though many argue even these are not legitimate and https should be secure!):
Anti-virus software can do this to scan requests to protect your computer. If you run Avast for example and have SSL scanning turned on (think it's on by default) and go to https://www.google.com and look at the cert you will notice it's been issued by Avast instead of by Google as usual. To do this requires the antivirus software to have installed an issuer certificate on your PC from which it can issue these replacement certs which your browser will still accept as real certs. Installing this issuer cert requires Admin access which you temporarily give when installing the anti-virus software.
Corporate proxies do a similar process to allow them to monitor https traffic from its employees. Again it requires an issuer installed on the PC using admin rights.
So basically it's only possible to use a proxy like you suggest for https traffic if you already have, or have had in the past, Admin rights in the PC - at which case all bets are off anyway.
The only other way to do this is to keep traffic on http using a proxy. For example if you request www.google.com then this normally redirects to https://www.google.com but your proxy can intercept that redirect request and instead keep the client->proxy connection on https, allowing the proxy to amend the request to strip out headers. This depends on the users not typing https, not noticing there is no green padlock and can be defeated with technologies like HSTS (which is automatically preloaded in some browsers for some sites like google.com). So not really reliable way to intercept traffic.
Many secure sites use X-Frame-Options to prevent clickjacking
Clickjacking
Clickjacking Defense Cheat Sheet
This prevents attackers from tricking users through transparent layers from performing actions they are unaware of on sites they didn't even know they loaded. Furthermore, this attack only works with frames that are directly served from the attacked/victim site's domain in the user's browser.
You may think that you can just reverse proxy the site and remove the frame busting header. But your proxy is not receiving or sending the end users cookies to the victim site. These secure sites rely on an active session, and as such will interpret the request from the proxy as coming from an unauthenticated user completely defeating the point of clickjacking.

With HTTPS, are the URL and the request headers protected as the request body is?

Just want to verify, when making a SSL connection (http post) to say:
https://www.example.com/some/path?customer_key=123123123
If you don't want anyone to know about customer_key, this approach will not work even if I am making a https connection correct?
All data that I want secured has to be in the request body right?
Quoting the HTTPS RFC:
When the TLS handshake has finished. The client may then initiate the
first HTTP request. All HTTP data MUST be sent as TLS "application
data".
Essentially, the secure SSL/TLS channel is established first. Only then the HTTP protocol is used. This will protect all the HTTP traffic with SSL, including HTTP headers (which contain the URL and cookies).
What may be visible in the handshake is the host name itself, since it's contained in the server certificate which will be visible in clear in the handshake (and it's often easy to guess the host name by looking at the destination IP address anyway).
When using Server Name Indication, the requested host name should also be visible in the server_name extension in the ClientHello message.
Otherwise, there may be a bit of ambiguity (for the eavesdropper) to guess the host name from the certificate if the certificate is valid for multiple host names (e.g. multiple Subject Alt. Names or wildcards). In this case eavesdropping the DNS request from the client might give the attacker a clue.
Reading other people's answers and comments, some mention issues about Referer (lost an r in the spec) and logs.
Referrers shouldn't be sent when going from HTTPS to HTTP (but they are often sent when going from one HTTPS site to another HTTPS site).
About the history: you'll just have to trust whoever can potentially get that key legitimately (i.e. your users) not to spread it around. If needed, have a strategy to change it once in a while.
About the logs: I was assuming you were after protection over the network. The URL (including query) will be in the logs indeed, but if someone is able to attack your machine so as to get the logs, you have more to worry about that your app keys.
One of the remaining potential weak points is how you give that link to the user. If it's embedded in a web-page served over plain HTTP, anyone who can read that page would be able to see it. You should serve such a page over HTTPS too. If you send that link by e-mail instead, I'd say all bets are off, since mail servers rarely encrypt the connections between themselves and users also often to access their e-mail account without any encryption.
EDIT:
In addition, if you're using client-certificate authentication, the client certificate will be visible if it is negotiated during the initial handshake. This may leak the name of the user accessing the website (often Subject DNs contain the user name). The client certificate will not be visible if it is sent during a re-negotiated handshake.
Only www.example.com will be visible to snoopers. The path section of the request is protected by SSL/TLS.
Obviously, you need to have sent original the hyperlink by HTTPS, too.
Request data will be sent after establishing Secure connection, so no worries with above URL, but remember your data is not encrypted, only channel between server and client is encrypted, if one can crack this channel, then can clearly see your data.
SSL is wrapper encrypted channel on top of your data. If data is plain, anyone who can crack the SSL can see your data clearly.
Revising my answer to NO!
Apparently only the host name is sent in clear text before the SSL connection is established.
http://answers.google.com/answers/threadview/id/758002.html
That Depends..
If you use a packet sniffer you cannot see the data sent over the wire. The main problem with this approach is that the request url is often saved in the server's log in plain text, the browser history keeps the url, URLs are passed in Referrer headers and maybe persisted by third party services (google analytics).

How to prevent SSL urls from leaking info?

I was using google SSL search (https:www.google.com) with the expectation that my search would be private. However, my search for 'toasters' produced this query:
https://encrypted.google.com/search?hl=en&source=hp&q=toasters&aq=f
As you can see, my employer can still log this and see what the search was. How can I make sure that when someone searches on my site using SSL (using custom google search) their search terms isn't made visible.
The URL is sent over SSL. Of course a user can see the URL in their own browser, but it isn't visible as it transits the network. Your employer can't log it unless they are the other end of the SSL connection. If your employer creates a CA certificate and installs it in your browser, they could use a proxy to spoof Google host names, but otherwise, the traffic is secure.
HTTPS protects the entire HTTP exchange, including the URL, so the only thing someone intercepting network traffic will be able to determine is that there was communication between the browser and your site (or Google in this case). Even without the innards, that information can be useful.
Unless you have full administrative control over the systems making the queries, you should assume that anything transpiring on them can be intercepted or logged. Browsers typically store history and cache pages in files on the local disk which can be read by administrators. You also can't verify that the browser itself hasn't been recompiled with code to log sites that were visited, even in "private" mode.
Presumably your employer provides you with a PC, the software on it, the LAN connection to its own corporate network, the internet proxy and corporate firewall, maybe DNS servers, etc etc.
So you are exposed to traffic sniffing and tracing at many different levels. Even if you browse to a url over SSL TLS, you have to assume that the contents of your http session can be recorded. Do you always check that the cert in your browser is from google and not your employer's proxy? Do you know what software sits between your browser and your network card, etc.
However, if you had complete control over the client, then you could be sure that no-one external to your https conversation with google would be able to see the url you are requesting.
Google still knows what you're up to, but that's a private matter between your search engine and your conscience ;)
to add to what #erickson said, read this. SSL will protect the data between the connected parties. If you need to hide that link from the boss then disable the browser caching of the sites visited, i.e. disable or delete the history data.

Resources