I’m writing some software that makes it easy to make basic changes to web pages. The software is designed to be very easy to install and use. If I do it right, it will be just one file that you install on your server. Even though I love Ruby and am not too fond of Perl, I have considered writing this program in Perl simply because of Perl’s ubiquity. I want there to be very few barriers to entry.
So my dilemma is this: SSL is a barrier to entry. A program like the one I am writing—with access to the source files of a website, with the ability to overwrite those files—needs to be secure. It needs SSL.
But setting up SSL on a server is difficult for those who haven’t done it before, or who are using less sophisticated servers. Also, if you want a real certificate, and not a self-signed one, then you have to pay for it.
I’ve been wracking my brain for an alternative to SSL. Something that runs in the browser and can communicate securely with the server. Something that could be built into the software, so you can use it if you don’t have SSL set up on your server.
The pages themselves should be publicly viewable anyway, so it’s just the commands to the server and the resulting responses that need to be protected. This would be handled by javascript in the background anyhow (AJAX), so these transmissions just need to be encrypted properly.
And that, it turns out is not really that hard. Imagine the following scenario: the server makes it’s public key available. The client encrypts the login credentials from the user using that public key. Now only the trusted server can read them, right? And then we can continue the communications that way, or to save computing resources we can safely send a symmetric key using this method and encrypt further communications with that? Right? Wrong. Sure, this is encryption, but it’s not very secure.
The server we are communicating with might not be the real one. How can we trust that the public key we are using is actually the right one? And it gets worse. A lot worse. All this javascript that’s running in the browser? Where, exactly did that come from? Well, from the server that we don’t trust. If you were fooled into visiting a spoof of your own website, or if the pages sent by your website had been altered by a third party, it might look like it was doing lots of fancy crypto, when it is in fact, just stealing your password.
So, after lots of thought I’ve come up with a theoretical method to get around these problems. It’s not perfect and certainly requires a fair level of user-awareness and intervention for it to work, but I think it would provide pretty good security if it could be implemented.
Here’s how it goes:
- The server side program is installed on the server with an asynchronous key-pair embedded in it.
- A bookmarklet is installed in the user’s browser with the public key embedded in it.
- All pages sent by the server are signed with the public key.
- The user runs the bookmarklet, which verifies that the page sent by the server has not been altered.
- The user now trusts the javascript code contained in the received page, which encrypts and decrypts any messages to the server, as described above.
This approach requires that each page received by the client must be checked for a signature before it is used. Unlike with SSL, at any time a third party could be tampering with the pages in transit, inserting arbitrary code (man-in-the-middle attack). So the onus would be on the user to verify each page before trusting it.
The basic principle at work here is that because javascript bookmarklets are installed by the user instead of presented by the server, they can be trusted to verify a signed web page. If a page has been digitally signed by a trusted source, then we can trust any further security measures implemented by code on the page.
Whether this method actually works will depend on how well such a signature-checking bookmarklet can be implemented.