How to identify https clients through proxy connection - node.js

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).

Related

Is a Http2 Cross-origin push request possible?

Say I have a server that serves an HTML file at the url https://example.com/ and this refers to a css file at the url https://test.com/mystyles.css. Is it possible to push the mystyles.css file alongside the html content as part of an HTTP2 connection, so that a browser will use this css content?
I have tried to create such a request using a self-signed certificate on my localhost (and I have pre-created a security exception for both hosts in my browser) by sending the html file when a request arrives at http://localhost/, and pushing the css with a differing hostname/port in the :authority or Host header. However, on a full-page refresh, the CSS file is fetched in a separate request from the server, rather than using the pushed css file.
See this gist for a file that I have been using to test this. If I visit http://localhost:8080/ then the text is red, but if I visit http://test:8080/ it is green, implying that the pushed content is used if the origin is the same.
Is there a combination of headers that needs to be used for this to work? Possibly invoking CORS?
Yes it is theoretically possible according to this blog post from a Chrome developer advocate from 2017.
As the owners of developers.google.com/web, we could get our server to
push a response containing whatever we wanted for android.com, and set
it to cache for a year.
...
You can't push assets for
any origin, but you can push assets for origins which your connection
is "authoritative" for.
If you look at the certificate for developers.google.com, you can see
it's authoritative for all sorts of Google origins, including
android.com.
Viewing certificate information in Chrome Now, I lied a little,
because when we fetch android.com it'll perform a DNS lookup and see
that it terminates at a different IP to developers.google.com, so
it'll set up a new connection and miss our item in the push cache.
We could work around this using an ORIGIN frame. This lets the
connection say "Hey, if you need anything from android.com, just ask
me. No need to do any of that DNS stuff", as long as it's
authoritative. This is useful for general connection coalescing, but
it's pretty new and only supported in Firefox Nightly.
If you're using a CDN or some kind of shared host, take a look at the
certificate, see which origins could start pushing content for your
site. It's kinda terrifying. Thankfully, no host (that I'm aware of) offers full control over HTTP/2 push, and is unlikely to thanks to this little note in the spec: ...
In practice, it sounds like it's possible if your certificate has authority over the other domains and they're hosted at the same IP address, but it also depends on browser support. I was personally trying to do this with Cloudflare and found that they don't support cross-origin push (similar to the blog post author's observations about CDNs in 2017).

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

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.

Using http protocol between servers

I have a configuration of two servers working in intranet.
First one is a web server that produces html pages to the browser, this html sends requests to the second server, which produces and returns reports (also html) according to some GET parameter's value.
Since this solution is un-secured (the passed parameter is exposed) I thought about having the html (produced by the first server) sending the requests for report back to the first server, there, a security check will be made, and the request for report will be sent to the reports server using http between the servers, instead of from browser to server.
The report's markup will be returned to the first server (as a string?), added to the response object and presented in the browser.
Is this a common practice of http?
Yes, it's a common practice. In fact, it works the same when your webserver needs to fetch some data from a database (not publically exposed - ie not in the webserver DMZ for example).
But you need to be able to use dynamic page generation (not static html. Let's suppose your webserver allows PHP or java for example).
your page does the equivalent of an HTTP GET (or POST, or whatever you like) do your second server, sending any required parameter you need. You can use cURL libraries, or fopen(http://), etc.
it receives the result, checks the return code, can also do optionnal content manipulation (like replacing some text or URLs)
it sends back the result to the user's browser.
If you can't (or won't) use dynamic page generation, you can configure your webserver to proxy some requests to the second server (for example with Apache's mod_proxy).
For example, when a request comes to server 1 for URL "http://server1/reports", the webserver proxies a request to "http://server2/internal/reports?param1=value1&param2=value2&etc".
The user will get the result of "http://server2/internal/reports?param1=value1&param2=value2&etc", but will never see from where it comes (from his point of view, he only knows http://server1/reports).
You can do more complex manipulations associating proxying with URL rewriting (so you can use some parameters of the request to server1 on the request to server2).
If it's not clear enough, don't hesitate to give more details (o/s, webserver technology, urls, etc) so I can give you more hints.
Two others options:
Configure the Internet facing HTTP server with a proxy (e.g.
mod_proxy in Apache)
Leave the server as it is and add an Application Firewal

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).

Is is possible to restrict a requesting domain at the application level?

I wonder how some video streaming sites can restrict videos to be played only on certain domains. More generally, how do some websites only respond to requests from certain domains.
I've looked at http://en.wikipedia.org/wiki/List_of_HTTP_header_fields and saw the referrer field that might be used, but I understand that HTTP headers can be spoofed (can they?)
So my question is, can this be done at the application level? By application, I mean, for example, web applications deployed on a server, not a network router's operating system.
Any programming language would work for an answer. I'm just curious how this is done.
If anything's unclear, let me know. Or you can use it as an opportunity to teach me what I need to know to clearly specify the question.
HTTP Headers regarding ip-information are helpful (because only a smaller portion is faked) but is not reliable. Usually web-applications are using web-frameworks, which give you easy access to these.
Some ways to gain source information:
originating ip-address from the ip/tcp network stack itself: Problem with it is that this server-visible address must not match the real-clients address (it could come from company-proxy, anonymous proxy, big ISP... ).
HTTP X-Forwarded-For Header, proxies are supposed to set this header to solve the mentioned problem above, but it also can be faked or many anonymous proxies aren't setting it at all.
apart from ip-source information you also can use machine identifiers (some use the User-Agent Header. Several sites for instance store this machine identifiers and store it inside flash cookies, so they can reidentify a recalling client to block it. But same story: this is unreliable and can be faked.
The source problem is that you need a lot of security-complexity to securely identify a client (e.g. by authentication and client based certificates). But this is high effort and adds a lot of usability problem, so many sites don't do it. Most often this isn't an issue, because only a small portion of clients are putting some brains to fake and access server.
HTTP Referer is a different thing: It shows you from which page a user was coming. It is included by the browser. It is also unreliable, because the content can be corrupted and some clients do not include it at all (I remember several IE browser version skipping Referer).
These type of controls are based on the originating IP address. From the IP address, the country can be determined. Finding out the IP address requires access to low-level protocol information (e.g. from the socket).
The referrer header makes sense when you click a link from one site to another, but a typical HTTP request built with a programming library doesn't need to include this.

Resources