Clarification for sending credentials via HTTPS - javascript

I have a website that is running on a server that integrates with the JIRA REST API. In order to authenticate requests, the site asks for the user credentials and sends those to JIRA to return back a session (see this).
Obviously, I don't want these credentials floating around in plaintext. While this will be run on an internal network that does not face the outside, it is still important.
My question is (excuse the poor explanation): Do I need to acquire an SSL cert for MY server even if JIRA is using HTTPS. In this instance, JIRA has a valid SSL cert and is secured by HTTPS.
Rough overview of the 'flow':
User enters user/pass and submits HTML form (index.html on nodejs) ->
-> Request (HTTP POST) is sent to my server w/ creds (nodejs)
-> My server sends a request to JIRA (HTTPS)
<- JIRA responds with JSON
<- Response is received by my server
<- My server sends data back to index.html
My thinking is I need HTTPS in this case due to this:
XX.XX.XX.XX <-/-> YY.YY.YY.YY <---> ZZ.ZZ.ZZ.ZZ
[XX] = client (index.html)
[YY] = server (nodejs)
[ZZ] = JIRA
In that 'diagram', the client sends the credentials to my server over HTTP, which is insecure. The server will then send the credentials to JIRA, which does have HTTPS enabled.
In this case, I'm assuming the credentials can be sniffed between X and Y, but not between Y and Z (I'm starting to confuse myself...)???
(P.S. I needed to put a server in the middle due to CORS limitations. Everything is working fine other than the HTTPS issue.)

You're right : the HTTP communication between the client X and your server Y is in clear text if there is no SSL configured on server Y. The credentials can be sniffed there, but can't be between Y and Z.
You can easily check with a tool like wireshark : https://www.wireshark.org/, if you run it on X or Y (or anything in between that might see the packets). Or the tcpdump command-line of you don't have a GUI (on Unix/Linux).
To secure this, you indeed need to switch to HTTPS on server Y, which implies to put a SSL certificate on it. You can test with a self-signed certificate (but the browsers will complain about it), and then put a valid certificate : as you seem to be in a corporate environment, you might have a corporate certificate authority trusted by your internal browsers.

Assume the credentials can be sniffed everywhere, encrypt everything.

Request (HTTP POST) is sent to my server w/ creds (nodejs) is HTTP, not HTTPS, that is a security issue. Get a certificate and setup HTTPS with TLS 1.2.
Remember is is the user who suffers if the security is bad, put yourself in their place, do you expect the services you use on the Internet to have best practice security?

Related

Google IAP Authentication for WebSockets

We have a Google http(S) LB in front of a Google Compute VM, and we are routing a subdomain to the backend which exposes only a wss endpoint. I couldn't find any example for javascript code how to use Authentication with Google IAP and OIDC Tokens.
Does Google IAP support query parameters for the authentication ?
I found this entry:
Bearer authentication for websocket
Thanks for any advice
There is no method in the JavaScript WebSockets API to customize WebSocket headers from JavaScript, you’re limited to the “implicit” auth (i.e. Basic or cookies) that are sent from the browser. Further, it’s common to have the server that handles WebSockets be completely separate from the one handling “normal” HTTP requests. This can make shared authorization headers difficult or impossible. One way to attain this is using a “ticket”-based authentication system.
When the client-side code decides to open a WebSocket, it contacts
the HTTP server to obtain an authorization “ticket”.
The server generates the ticket. It typically contains some sort of
user/account ID, the IP of the client requesting the ticket, a
timestamp, and any other sort of internal record keeping you might
need.
The server stores this ticket (i.e. in a database or cache), and
returns it to the client.
The client opens the WebSocket connection, and sends along this
“ticket” as part of an initial handshake.
The server can then compare this ticket, check source IPs, verify
that the ticket hasn’t been re-used and hasn’t expired, and do any
other sort of permission checking. If all goes well, the WebSocket
connection is now verified.
Refer to the link for websocket security and related stack posts HTTP headers in websockets client API and Websocket authentication.

how can I force fetch to accept a self-signed certificate in a web app front end?

I am prototyping a simple web app front end that needs to fetch JSON data from my server. The server itself works fine -- I can click on the link, and the JSON data shows up in the browser. But the following simple script fails:
fetch('https://x.x.x.x:8000') // MY URL FAILS
// fetch('https://jsonplaceholder.typicode.com/todos/1') // ALTERNATE URL WORKS
.then(function() {
alert("Successful")
})
.catch(function() {
alert("Failure")
});
I'm completely new to this sort of front-end work (and to Javascript in general), so I might be overlooking an obvious reason, but the two that come to mind are
my server uses a self-signed certificate for testing purpose; and/or
I'm using a non-standard port.
The first of these possible explanations seems more likely.
Accessing the web page generates a bunch of errors, none of which mean anything to me (except for not finding the favicon):
I will temporarily post the full URL in a comment below, in case anyone else wants to see what happens, but I would delete it once a working solution is suggested.
To answer your question as asked, no, you definitely can't use fetch to force the client (browser) to ignore cert errors. Especially in cross-origin requests (and going from one port to another is cross-origin), that would be a HUGE security hole. It would allow anybody who could get a man-in-the-middle position on a victim's network (not hard) to steal information from the victim's HTTPS connections using fraudulent certificates to intercept the HTTPS requests and responses.
You might be able to force server-side JS (in Node or similar) to ignore cert validation errors, since in that case you (hopefully!) control the code the server is running. But it doesn't look like that's what you're doing, and in a web page, somebody else (the server owner) controls what code you (the browser) are running, so you definitely can't let that code turn off important security features!
Attack scenario for if JS could turn off cert validation:
Suppose you and I both control web servers. I, a malicious attacker, would like to intercept the traffic between your users and your web server. I even have a man-in-the-middle (MitM) network position on some of your users! However, you are of course using TLS (via HTTPS), so I can't decrypt or modify the traffic.
However, your users sometimes connect to my server as well, not knowing it is malicious (maybe I mostly use it to serve relatively innocuous stuff, like a comment system or analytics tools, so lots of sites embed my scripts). My server can tell when a browser requests content from an IP address where I could launch an MitM attack, and serve them malicious scripts.
Now, in the real world, this doesn't matter! Sites don't trust other sites, because of the Same-Origin Policy, a critical browser security feature. My site (or the scripts I serve) can cause your users to submit requests to any other server that I choose, but they can't read the responses (if the other server is cross-origin), and they can't turn off certificate validation so my MitM position is mostly useless.
However, suppose that there was a way - as you propose - for scripts to tell the browser "it's ok, just trust this one particular self-signed cert when making this request". This changes everything. My MitM host will generate a self-signed cert (and corresponding private key) for your site, and send the cert to my own web server. When a potential victim loads a script from me, it only only contains instructions to make HTTP requests to your site, it also specifies that the browser should trust the self-signed certificate that my MitM node generated.
The victim's browser would then start the request, attempting to establish a TLS connection to your server. My MitM node would intercept the request, and reply with its self-signed certificate. Normally the browser would reject that, but in this case it doesn't because you created a way to tell browsers to accept a particular self-signed cert. Therefore, the victim's browser trusts my self-signed certificate. The actual request never even makes it to your server. The victim's browser, believing itself to be interacting with the legitimate server (yours) rather than with my MitM host, sends an HTTP request containing secrets such as cookies and/or API keys / bearer tokens / login credentials / etc. My MitM intercepts that (as it's intercepting all traffic), decrypts it (because it is in fact one end of the TLS tunnel, this is trivial), and can access the victim's account on your server. (My MitM host can also duplicate the responses from your server that the victim would usually see, to keep them unsuspecting. The MitM host can even tamper with this responses, if I want it to mislead the user.)
The usual way to solve this is to install the server's certificate as trusted in the browser (or in the OS). That way, the browser will recognize the certificate's issuer (itself) as valid, and consider the certificate valid.
What happens if you go to https://x.x.x.x:8000/ in the browser directly? If you get a certificate error, well, that's your problem: the browser doesn't trust the certificate of the server hosted on that port. You should have an opportunity to temporarily or permanently trust that certificate (exact details will depend on the browser).
Note that, of course, doing this on your own computer won't fix it for anybody else's computer. They'd need to manually trust your certificate too.
The actual solution is, of course, to install a trusted certificate. Perhaps you should try Let's Encrypt or similar, for a nice free cert that every client will trust without extra shenanigans?
Just had the same problem and stumbled upon the solution by accident. It is possible by just making the user open the self-signed site, click on 'Show more' and 'Accept the risk and continue'. After doing that, fetch requests go through like nothing ever went wrong.
It works on Firefox:
and Chrome:
This method just has the caveat that you have to do the setup, and on Chrome it displays 'Not secure' even when the rest of the page is secure.
But if you need HTTPS locally, this works like a charm. Hope this helps the people who came here from Google :)
EDIT:
Also worth mentioning, I tested it on localhost but it works everywhere.

Download client over ssl but connect to arbitrary IP

In browsers, is it possible to request javascript for a web-client over https, but have said client connect with a websocket to an IP either:
without ssl
with a self-signed certificate (without previously adding the certificate to the browser)
From what i read, it simply isn't. Mixed content is forbidden, and self-signed certs are not accepted, unless the user goes through a weird and dangerous looking menu, to manually accept the certificate in the browser. It's not like the typical user has the qualification to check a certificate anyways.
This would directly imply, that for this case, encryption cannot be used at all, sadly.
Isn't there something i overlooked? If the websocket connection wasn't over ssl, the client could even just encrypt itself, inside a non-ssl connection. The server it's connecting to won't have a certificate from a CA, it typically won't even have a domain.
Edit: some background
The desired situation is as follows:
a user enters a website, and gets served a client via a secure connection (this is from a static domain, so having a certificate from a CA is no problem here. However, using https apparently disallows non-ssl-connections from that site, and the client accordingly).
the client then opens a websocket connection to a server IP, which may arbitrarily vary (can even be multiple servers at the same time), and typically has no domain. For this, i can only come up with a few scenarios:
no ssl (forbidden, mixed content)
self-signed certificate, preferably the client would get the public key from the main domain, and have that pinned (self-signed produces warnings, and users can't really be required to manually install certificates just for this)
Some CA offers free and automated certificates for IPs, which we can depend on, and just generate new certificates whenever necessary, without any human action needed (i didn't consider this before, because i simply didn't think any CA would offer such a thing. Maybe there is, but so far, i've not encountered any)

What is the correct CORS entry for limiting an http:// connection to a remote, hosted web server from a grunt web server on a home network?

I've setup a remote, hosted javascript server (DreamFactory Server http://www.dreamfactory.com/) that responds via REST API's.
Locally, I'm running an Angularjs application through the grunt web server via $grunt serve
https://www.npmjs.com/package/grunt-serve
I have setup CORS on the remote server to allow '*' for multiple http:// connection types. THIS WORKS CORRECTLY.
My question is how I can limit the CORS configuration to only allow a connection from my home, grunt web server?
I've tried to create an entry for "localhost", "127.0.0.1", also my home Internet IP that is reported from whatismyip.com, the dns entry that my provider lists for my home IP when I ping it, a dyndns entry that I create for my home internet IP... None of them work, except for '*' (which allows any site to connect).
I think it is an educational issue for me to understand what that CORS entry should look like to allow ONLY a connection from my home web server.
Is this possible? If so, what and where should I be checking in order to find the correct entry to clear in the CORS configuration?
-Brian
To work and actually apply restrictions, the client requesting the connection must support and enforce CORS. In an odd sort of way (from a security point of view), restricting access using CORS requires a self-policing client (one that follows the prescribed access rules). This works for modern browsers as they all follow the rules so it generally works for applications that are served through a browser.
But, CORS access restrictions do not prevent other types of clients (such as any random script in any language) from accessing your API.
In other words, CORS is really about access rules from web pages that are enforced by the local browser. It doesn't sound like your grunt/angular code would necessarily be something that implements and enforces CORS.
If you really want to prevent other systems from accessing your DreamFactory Server, then you will need to implement some server-side access restrictions in the API server itself.
If you just have one client accessing it and that client is using "protected" code that is not public, then you could just implement a password or some sort of logon credentials and your one client would be the only client that would have the logon credentials.
If the access is always from one particular fixed IP address, you could refuse connections on your server from any IP address that was not in a config file you maintained.
You can't secure an API with CORS, for that you will need to implement an authentication scheme on your server. There's essentially 4 steps to do this.
Update the headers your server sends with a few additional Access-control statements.
Tell Angular to allow cross-domain requests.
Pass credentials in your API calls from Angular.
Implement an HTTP Authentication scheme on your web server or in your API code.
This post by Georgi Naumov is a good place to look for details of an implementation in Angular and PHP.
AngularJS $http, CORS and http authentication

How to call a web api via ajax over SSL from a local html file?

I have a local html file in may desktop accessing a web api (JAX-RS) that responds with some JSON data. I enabled CORS and everything works fine, but only without SSL. How can I do to make it work with SSL? I use a self-signed certificate and can call this web api from a WPF application, but from a JavaScript application (standalone html file), when Chrome sends the OPTIONS pre-flight before the POST, the request seems not to even reach the server. I also tried to import the self-signed certificate in the browser, but nothing has changed.
The preflight request is not allowed to include an entity body or credentials. If you are using preflighted requests then you cannot use two way SSL.
The solution is to change the server to make the certificate optional. I've only done this using Apache HTTP server or Tomcat but I assume other servers are also capable of this.
In apache the setting should be changed to
SSLVerifyClient optional
and in Tomcat the SSL settings should be changed to
clientAuth="want"
Without this change only CORS simple requests will work.

Categories

Resources