Is there a Javascript equivalent of .NET HttpWebRequest.ClientCertificates? - javascript

I have this code working in C#:
var request = (HttpWebRequest)WebRequest.Create("https://x.com/service");
request.Method = "GET";
// Add X509 certificate
var bytes = Convert.FromBase64String(certBase64);
var certificate = new X509Certificate2(bytes, password);
request.ClientCertificates.Add(certificate, "password"));
Is there any way to reproduce this request in Javascript? Third-party libraries would be fine for my purposes.

In browser-JavaScript there is no hope of doing anything like that.
The XMLHttpRequest interface gives you limited capabilities to customise the connection. You don't get any opportunity to influence SSL negotiation and you don't get the ability to make a request to a different domain than the one you're running on (for very good security reasons).
You could get around that and use a low-level socket, with complex libraries to implement HTTPS over the top of it, except that JS doesn't give you any access to low-level sockets either. Browser scripts just aren't expected or trusted to do that kind of thing: again, there are some serious security issues to worry about if any web page can make you send random connections to other servers (including ones on your private local network).
HTML5 gives you WebSocket, which can be used for low-level, low-latency connections, but it is deliberately incompatible with other services, to stop you attacking them. In general, anything you want a browser to talk to, whether that's via XMLHttpRequest, WebSocket or Flash Socket, will have to be deliberately set up to listen for browsers.

You may check out the opensource Forge project. It implements SSL/TLS in JavaScript, including the ability to specify client-side certificates.
http://github.com/digitalbazaar/forge/blob/master/README
This project makes use of the raw Socket interface made available via Flash, and implements the actual TLS layer in JavaScript. However, because Flash is used, the server you are contacting will need to provide a cross domain policy file. Included in the Forge project is an Apache module that server administrators can install to provide this policy file easily.
If you're looking for a solution that doesn't involve a server you have administrative privileges to -- then it does look like you're out of luck. Like the other poster, bobince, said, all of new approaches to communicating over raw sockets (or similar), via the web browser, require web servers to manually "opt-in".

Related

How to restrict the web client to a certain domain?

I have a problem that people are cloning my website front and imitate calls to my API from their own domains to abuse my service. The solution I came up with is for Angular client to check the URL it works on, encrypt it and add as a header to API call. Obfuscate the JS code to prevent reverse engineering. This way API will receive an encrypted header and make sure that the domain is the proper one.
So on the client side
headers.append(`CustomHeader`, this.encryptDomain());
and on the server side
var domainEncrypted = Request.Content?.Headers?.GetValues("CustomHeader").FirstOrDefault();
var domainPlain = Decrypt(domainEncrypted);
if (domainPlain != myDomain)
{
return BadRequest();
}
Can you please help me with code samples to match JS and C# encrypt and decrypt algorithms? So that encryptDomain works on JS side and Decrypt works on the C# side. I am aware that this is not a perfect solution, but I want to try. And if anyone has a better idea, you are welcome.
Edit: apparently what I want to do is similar to JScrambler domain lock feature
TLDR
It is not possible to prevent communicate with your API through different (cloned) clients guaranteed way in cases when white-lists of IP addresses can't/shouldn't be used.
Why
Think about it that way. You have a server that has some identification rule - client should have some identifier that marks it as trusted. In your question it is a domain.
Domain is a public information that could be passed in HTTP header or in the body of your request, it is easy, but also it will be easy for clients to replace this information on their side.
And if you use any type of cryptography to provide more secured identification mechanism - you just making it harder to hack it and again pretend as trusted client, because every mechanism you use on the client side could be reverse-engineered by a hacker. Just look at this question.
One think you can use to guaranteed access restriction is to use white-list of IP addresses on server-side, because IP address is a part of TCP/IP transport level protocol and it has "handshake" process to identify communicated points to each other, and it is kind of hard to replace it. Check this question for details.
So what can you do?
CORS
Setup CORS policy is a first step to create a trusted client-server communication. Most of browsers are support CORS policies, but of course client may be not a browser. And the questions was not about browser-server communication, but I should mention that because browser is a client too.
Client-side encryption
You can use encryption, but I don't see any reason to do that because any request to server could be read through your legal client (website). So even if you encrypted it - any person has a key and a crypto algorithm on their side to pretend as trusted client. But if you want to...
You need to create unique key every your request to make life of pretenders little harder. To make it you need few ingredients:
Public key for key generation (encrypted) on the client side
Obfuscated key generation JS code
Private key for decrypt generated key on the server side
JS-side RSA crypto libraries could be googled easily (for example)
Obfuscation libraries could be found just using google too (like this)
Server-side decryption could be done with System.Security.Cryptography namespace if you use C# backend.
Basically, more complex key-generation algorithm you make and more obfuscated code you make - more hard for hacker to pretend himself as a trusted client. But as I said there is no guaranteed way to completely identify trusted client.
You cannot prevent people from copying your website's FE assets... They are supposed to be publicly available. You could try to make it a little harder by spliting your built app in more chunks (with angular's lazzy-loading or by manipulating webpack's config). Still, Browsers require code in plain text, so although this makes it a little harder it does not prevent copying.
When we build angular for production it already does code obfuscation through its optimizations (minification, tree-shaking and so on).
To mitigate the problem of people misusing your Server resources, you need to implement robust practices on Back-End request authorization and some miss-usage detection.
Configuring CORS would not work, as you reported attackers are using BE proxies.
Make sure your request's authentication is solid. A market standard approach is the use of a JWT payload embedded in the Authorization Header of each request. It is simple, reliable and resource-inexpensive.
I would also recommend the implementation of request throttling. But this is a separated question.
If your authentication is already solid, you would need to detect when your real users are misusing your system. There are many tools to monitor traffic (like azure's) but there is no umbrella definition for "unusual traffic". Detection of "unusual traffic" is what you would need to custom built for the specifics of your system. Once you have a network traffic tool in place that should help you getting started.
Couple of solutions for you. Firstly you can block by applying a CORS policy on server. If you still want to do from code then you can block on this basis of hostname in c# like this.
var hostname = requestContext.HttpContext.Request.Url.Host;
if (hostname != myDomain)
{
return BadRequest();
}

WebSockets vs XHR for data transfer

I am designing an architecture for a web application using Node.js, and we need to be able to send medium size files to the client from a gallery. As a user browses the gallery, they will be sent these binary files as fast as possible(for each gallery item). The files could go up to 6Mb, but probably average around 2Mb.
My client is insisting that we should use websockets for data transfer instead of XHR. Just to be clear, we don't need bi-directional communication.
I lack the experience in this domain and need help in my reasoning.
My points so far are the following:
Using WebSockets breaks any client-side caching that would be provided by HTTP. Users would be forced to re-download content if they visited the same item in the gallery twice.
WebSocket messages cannot be handled by/routed to proxy caches. They must always be handled by an explicit server.
CDNs are built to provide extensive web caching, intercepting HTTP requests. WebSockets would limit us from leveraging CDNs.
I guess that Nodejs would be able to respond faster to hundreds/thousands of XHR than concurrent websocket connections.
Are there any technical arguments for/against using websockets for pure data transfer over standard HTTPRequests. Can anyone nullify/clarify my points and maybe provide links to help with my research?
I found this link very helpful: https://www.mnot.net/cache_docs/#PROXY
Off the top of my head, I can see the following technical arguments for XHR besides that it uses HTTP and is therefore better at caching (which is essential for speed):
HTTP is the dedicated protocol for file downloads. It's natively built into browsers (with the XHR interface), therefore better optimised and easier to use for the developer
HTTP already features a lot of the things you'd need to hand-craft with websockets, like file path requests, auth, sessions, caching… All both on the client and server side.
XHR has better support even in older browsers
some firewalls only allow HTTP(S) connections
There do not seem to be any technical reasons to prefer web sockets - the only thing that might affect your choice is "the client is king". You might be able to convince him though by telling him how much he has to pay you to reimplement the HTTP features on a websocket connection. It ain't cheap, especially when your application gets more complex.
Btw, I wouldn't support your last point. Node should be able to deal with just as many websocket connections as HTTP connections; if properly optimised all things are even. However, if your server architecture is not based solely on node, there is a multitude of plain file serving applications that are probably faster than node (not even counting the HTTP caching layer).

In a browser environment, is it possible to obtain list of SSL certificates in JavaScript?

In order to connect to a third party application, I have to give my users the capability to select one of their installed SSL client certificates and transfer it to the third party which is used by the application server. (My web application does not require SSL, it is the third party that require SSL certificates).
It seems to me that access to this list of certificates is only possible by the browser itself when connecting to a service that require SSL. Is it possible to launch the same dialog box through Javascript or is there any way for a web application to browse the SSL store of the end-user ?
If it is not possible, can I simply open a file dialog box and upload the client certificate as any standard file ?
I have to support any browser from IE9 and no plug-ins are allowed in our application.
Thanks.
If it is not possible, can I simply open a file dialog box and upload the client certificate as any standard file ?
Firstly, that's not the way SSL/TLS client authentication works at all. It's simply not a matter of uploading the certificate. The private key matching the certificate is used to sign some content (in the CertificateVerify TLS message) during the TLS handshake. That's what performs the authentication.
Coming back to your main question, for security reasons, the SSL/TLS stack is handled outside the scope of the JavaScript code. Selecting the client certificate is part of that.
You could potentially have some sort of API to let the JavaScript code access some of the cryptographic features of the browser (and there has been work in this area). However, there would be security considerations to take into account.
Even if certificates only contain public information to some extent, that doesn't mean it's public information that is to be distributed to anyone in the world, at least not necessarily in conjunction with the act of browsing any website.
If you had the ability to list the user's list of certificate from the JavaScript code sent by your server, you'd certainly have the ability to send that list back to yourself almost transparently with an Ajax call. While some people are concerned about the privacy implications of being tracked by cookies, being tracked by which client certs you may have takes this to another level (e.g. Subject DN with CN=John Smith and Issuer DN with CN=Department/Ministry of Health/Defence: that would be a bit of a giveaway).
My web application does not require SSL, it is the third party that require SSL certificates.
Here, you're not saying whether that third party is accessed directly by the user's browsers, or if you expect the users to delegate their credentials for you to interact with that third party (without direct user involvement).
If the users have direct access to that third party (via another request), their browser should prompt them for the certificate they with to use.
If it's about credential delegations, that's another problem entirely, since users you never give you the private key for their own client certificate to be able to sign in their name. (It's might be technically possible for users to just give you their PKCS#12 file, for example, but it defeats the point of putting up in place this sort of authentication in the first place).
There has been work done about authentication delegation with certificates using proxy certificates (RFC 3820). Essentially, your EEC (End-Entity Certificate) is used as a mini-CA, despite not having the CA flags, to issue a short-lived certificate with the remote party will accept. This sort of mechanism is generally not well integrated in browsers.
Another, more realistic approach, would be to look into the world of SSO, SAML and Shibboleth, for example. That does work with existing browsers, but the overall architecture is a bit different (so you'll need to discuss that with the third party).
The certificate isn't part of the DOM, so no, this won't be possible.
In a browser environment, is it possible to obtain list of SSL certificates in JavaScript?
The WebCrypto API allows you to discover some things, like shared and derived keys. But looking at their charter and use cases, its not clear to me if they allow enumeration and discovery of certificates.
I see it was discussed in the past and an issue was raised. Here's the discussion: Crypto-ISSUE-15: Discovering certificates associated with (private) keys. But I can't find anything on Issue 15 in the WebCrypto Tracker.
Also see Will the WebCrypto API allow discovery/enumeration of certificates? question on the WebCrypto Mailing list. Hopefully there will be a simple, YES/NO answer.
But don't be surprised if its not available through WebCrypto. The browser security engineers have a particular way of looking at things, and that usually does not include client certificates. Client certificates would effectively stop MitM attacks (see, for example, Origin Bound Certificates), and browsers don't make stopping MitM a priority. Instead, they are OK with mishandling credentials like passwords; and they opt for a One Time Password (OTP) using U2F.
In a reality stranger than fiction, the browsers will even (1) use Public Key Pinning for HTTP, and then (2) break a known good pinset because the user was phished! You can't make this stuff up...

Can a browser communicate with another browser on the same network directly?

I'm playing around trying to find a way to communicate between two browsers on the same network to establish WebRTC without a server roundtrip (no STUN/ICE/TURN). Basically an alternative to the approach found here, where the "handshake" is done via copy/mail/pasting.
After sifting through all the cross-browser-communication examples I could find (like via cookies or WebTCP) plus a bunch of questions on SO (like here), I'm back to wondering a simple thing:
Question:
If Alice and Bob visit the same page foo.html while on the same network and they know each others' internal assigned IP addresses, are there any ways they can communicate purely with what is available on the browser?
This excludes non-standard APIs like Mozilla TCP_Socket_API, but other than that all "tricks" are allowed (img tags, iframes, cookies, etc.).
I'm just curious if I can listen to someone on the same network "broadcasting" something via the browser at all.
Edit:
foo.html will be on static server, no logic, no ICE, no shortcut.
Edit:
Still not a solution but a websocket server as Chrome extension comes closer. Example here: almost pure browser serverless WebRTC
Yes, you can establish a direct connection between two browsers over the local network using WebRTC. It requires the use of ICE, but that does not mean that an outside STUN or TURN server is needed. If the browsers are on the same network, ICE will succeed with only the local candidates of each browser.
STUN/TURN is needed only in order to guarantee that two endpoints can establish a connection even when they are in different networks and behind NATs.
In fact, if you use most of the WebRTC example applications (such as apprtc) with two browsers connected in a local network, ICE is most likely to select and use the pair of local addresses. In this case a channel allocation on a TURN server will be made, but it will not get used.
In your WebRTC application, you can disable the use of STUN/TURN by passing empty iceServers when you create the PeerConnection.
While the MDN documentation lists WebSocketServer as a client API, I don't think this is accurate (maybe they wanted to document there how to write a server).
At the moment, I know no standard way to create a server socket on a web browser. I know a couple of attacks to scan the local network but most of them rely on an active server outside the network, that is you connect to a server and get JavaScript back which opens a WebSocket connection. Via that connection, I can take full control over the client and have it open more WebSockets with local IP addresses to scan the internal network.
If internal web sites don't implement CORS correctly (see here), I can access all internal web sites where the current user is currently logged in. That is a devious attack vector which allows external attackers to browser internal documents without cracking anything. This page has a demo of the attack.
Even Flash won't let you create a server socket.
If you allow a Java applet and the Java version on the client is very old or the user blindly clicked "OK", then you can create server sockets.
Related:
Socket Server in Javascript (in browsers)?
This could be explained easily. The answer is it's not possible. In order for alice and bob to communicate at all without a third-party, at least one of them needs to be listening for incoming connections. Not possible using a standard web browser alone.
You can take a look at this
https://github.com/jed/browserver-client
I think that you can easily create an http server with javascript and send messages from one browser to another
With Nodejs you can achieve the same.

Self-signed certs -- helping users know they need to add root CA to trusted cert store

I have a desktop product which uses an embedded webserver which will use self-signed certs.
Is there something that I can put in a web page that would detect that they haven't added the root CA to their trusted list, and display a link or DIV or something directing them how to do it?
I'm thinking maybe a DIV that has instructions on install the CA, and a Javascript that runs some test (tries to access something without internal warnings??), and hides the DIV if the test succeeds. Or something like that...
Any ideas from the brilliant SO community ? :)
Why do you want to do this? It is a bad idea to train users to indiscriminately install root CA certificates just because a web site tells them to. You are undermining the entire chain of trust. A security conscious user would ignore your advice to install the certificate, and might conclude that you are not taking security seriously since you did not bother to acquire a certificate from an existing CA.
Do you really need HTTPS? If so, you should probably bite the bullet and make a deal with a CA to facilitate providing your customers with proper CA signed server certificates. If the web server is only used for local connections from the desktop app, you should either add the self-signed certificate to the trusted list as part of the installation process, or switch to HTTP instead.
Assuming you know C# and you want to install a pfx file.Create a exe that will be run from a url.Follow this URL
and this
The only idea I have is to use frames and some javascript.
The first element of the frame will act as a watchdog waiting x amount of time (javascript setTimeout) before showing your custom ssl failure message to the user with hyperlinks or instructions to download the self-signed cert.
The second frame element attempts the https connection and if successful resets the watchdog frame so that it never fires. If it fails (assume https cert validation failed) the watchdog message would then fire and be presented to the user.
Depending on your browser you will most likely still see some security warning with the approach but you would at least be able to push your own content without requiring users to run untrusted code with no proper trust chain (This would be much much worse from a security POV than accepting the cert validation errors and establishing an untrusted ssl session)
Improvements to the concept may be possible using other testing methods such as XMLHttpRequest et al.
You should not do this. Root certificates are not something you just install, since adding one could compromise any security given to you by https.
However if you are making a desktop app then just only listen to 127.0.0.1. That way the traffic never leaves the users computer and no attacker can listen in.
You might try to add some (hidden) Flex element or Java Applet once per user session.
It will just download any https page of your server and will get all information about connection:
com.sun.deploy.security.CertificateHostnameVerifier.verify()
or
javax.security.cert.X509Certificate.checkValidity()
I suppose Flex (which is more common to users) shoul have similar ways of validating https certificate from user's point of view. It should also share OS' trusted cert. store while Java might have its own.
Since the server is running on the client machine (desktop product) can it not check the supported browsers for installed certs using winapi/os functions? I know Firefox has a cert database in the user's profile directory and IE probably keeps information in the registry. It wouldn't be reliable for all browsers but if the server simply chooses between "Certificate Found" and "Please ensure you have installed the cert before continuing" then no harm is done as the user can choose to continue either way.
You could also simplify matters by providing an embedded browser as well (ie, gecko), this way you only have 1 browser to deal with which simplifies a lot of things (including pre-installing the root CA).
To recap: you are setting up webservers on desktop apps; each desktop will have its own webserver, but you want to use SSL to secure the connection to that webserver.
I guess there are several problems here with certificates, one being that the hostname used to access the desktop has to match the certificate. In this case you have little choice but to generate certificates on the client. You'll need to allow the user some way to specify the host name in case the name used by outsiders can't be detected from the host itself.
I'd also suggest allowing for an admin to install a trusted cert, for those who don't want to rely on self-signed certs. This way you can also offload the cost of trusted cert maintenance to the admins who really want it.
Finally, in my experience browsers either allow or refuse the self-signed cert and there is no way for the server to know if the cert is denied, or temporarily accepted, or permanently accepted. I assume there must be a mechanism somewhere to handle SSL failures but typical web programming doesn't operate at that layer. In any case, the only thing a webserver can do if SSL fails is to fallback to non-SSL, and you've indicated in a comment that you can't have anything non-SSL. I think you should try to have that restriction lifted; a non-SSL start page would be extremely helpful in this situation: it can test (using frames or images or JSON or AJAX) the https connection, and it can link to documentation about how to set up the certificate, or where to download an installer for the cert.
If the browser won't connect because of a self-signed cert, and you're not allowed to use plain HTTP at all, by what other means could you communicate with the user? There are no other channels and you can't establish one because you don't have any communication.
You mentioned in a comment writing a win32 app for installing the cert. You could install a cert at the time you install the application itself, but that doesn't help any remote browsers, and a local browser doesn't need SSL to access localhost.
We've been working on an opensource JavaScript project, called Forge, that's related to this problem. Do you have a website that your users could access? If so, then you could provide a secure connection to those desktop apps via your website using a combination of Flash for cross-domain + JavaScript for TLS. It will require you to implement some web services on your website to handle signing certificates the desktop app certificates (or having your desktop apps upload the self-signed certs so they can be accessed via JavaScript). We describe how it works here:
http://blog.digitalbazaar.com/2010/07/20/javascript-tls-1/
An alternative to setting up a website, but is less secure because it allows for a MiTM attack is to host the JavaScript+Flash directly on the desktop app server. You could have your users hit your desktop app over regular http to download the JS+Flash+SSL cert, but then start using TLS afterwards via the JS. If you're on a localhost connection the MiTM attack might be a little less worrisome -- perhaps enough for you to consider this option.
An ActiveX control could do the trick. But I really didn't chime in to help with the solution, more to disagree with the stance that what you are doing is a security risk.
To be clear, you are needing a secure cipher (hopefully AES and not DES), and are already in control of your endpoints, just not able to completely rule out promiscuous-mode network sniffers that could catch clear-text passwords or other sensitive data.
SSL is a "Secure Socket Layer", and by definition, is NOT dependent upon ANY certificates.
However, all effective modern ciphers require it to authenticate the tunnel endpoints, which is not always a necessity for every application; a frustration I have dealt with in numerous back-end datacenter automation routines using web service APIs to manage nodes, where the "users" were actually processes that needed encrypted key exchange prior to a RESTful command negotiation.
In my case, the VLANs were secured via ACLs, so I really "could" send clear-text authentication headers. But just typing that made me throw up in my mouth a little bit.
I'm sure I'll get flamed for typing this, but I'm extremely battle-hardened and would've made the same comments to you in years 10-15 of my IT career. So I empathize with their worries, and very much appreciate if they are passionate enough about security to flame me. They'll figure it out eventually.....
But I do agree with the fact that it is a BAD idea to "train" users to install root CA's on their own. On the other hand, if you use a self-signed cert, you have to train them to install that. And if a user doesn't know how to determine if a CA Cert is trustworthy, they definitely won't be able to determine a self-signed cert from a CA Cert, and thus either process would have the same effect.
If it were me, I would automate the process instead of having it assist the end-users, so that it becomes as hidden from them as possible, just like a proper PKI would do for an enterprise.
Speaking of which, I just thought of a potential solution. Use the Microsoft PKI Model. With Server 2012 R2, you can deliver trusted keys to endpoints that are not even domain members using "device control" via "workspaces", and the client machines can subscribe to multiple workspaces, so they are not committed solely to yours if they subscribe. Once they do, and authenticate, the AD Certificate Services Role will push all root CA Certs necessary, as are present in active directory, or specified LDAP server. (In case you are using offline CA servers)
Also, I realize this thread is like 7 years old, but am sure it still gets referenced by a good number of people needing similar solutions, and felt obligated to share a contrasting opinion. (Ok Microsoft, where's my kickback for the plug I gave you?)
-cashman

Categories