TO BE CLEAR: I'm NOT looking to write my own protocol, or my own client-side implementation. I'm looking for an EXISTING protocol, part of the HTTP standards, that is generally ALREADY supported by common browsers. (Such a thing may not exist.)
I have an HTTP application that needs some protection from man-in-the-middle threats. (The server needs to be able to prove the authenticity of its responses to the client.) Encryption is irrelevant and unnecessary. Also, the hardware budget is pretty small, given the expected hit rate.
Normally, I'd just get a cheap certificate and enable SSL on the web server, but a load-generator killed both Apache and Nginx (and I don't expect any other web servers to be do any better). For non-HTTP service, the load is fine. I also tried configuring the HTTPS server to negotiate a null encryption cipher. That helped a little, but the SSL handshake is still too heavy.
A simple SHA-1 signature (generated by the server) over the response document, verified by the client, would suit me just fine. I wrote a simple Python FastCGI script and HTTP client to test just the bare SHA-1 operation--it adds some load, but not much, not even close to what SSL with null encryption adds. In practice, though, my clients won't be Python scripts, they'll be web browsers.
(AGAIN, TO BE CLEAR: I'm not proposing my own crypto protocol, the Python script was just a test of how much load the SHA-1 hash generates, by itself, versus the rest of the SSL negotiation. I cannot use a custom client-side implementation, because I have no way of installing it for all of the client browsers.)
So: Are there any EXISTING lightweight (compared to SSL) server authentication protocols for HTTP?
What you're looking for doesn't really exist. The lightweight mechanism built in to the browsers and servers is SSL.
If you're injecting a SHA1 in your content, and I'm MITMing you, what keeps me from just fixing the SHA1 as well?
Some workarounds include:
make ajax handshake mechanism and HMAC page validation. If you insert page imitating public / private key mechanism - third party can easily tamper with data, else MITM can also easily fix data hash. Remember that cryptography is rarely done right, so stand on others shoulders and use proven mechanism.
I suggest that you use ssl, with tuned nginx (look at ssl sessions and cyphers). Otherwise your MITM will be just parody and wasted work and cpu cycles.
Depending on how much control you have over the client, you could replace the trivially breakable SHA1 with HMACSHA1. The problem with HMACs is that they require a secret key to be known on both sides of the transmission beforehand. Anyone with access to your client would probably be able to extract the secret-key by disassembling it.
We have used this before for cases where we had two separate web servers, one which provided the website, and one which processed orders, which were located in geographically distinct datacenters. This made sure nobody would be able to send requests directly to the fulfillment server and bill people.
The problem with using an obscure or bespoke protocol for signing is that it's not already built into the browser(s). This means you need to provide the validation logic to the remote browser. If you don't have protection from a MITM attack when you deliver that code/logic, then what stops the MITM from just replacing the validation code with something that returns "VALID" no matter what it sees - and/or, what stops the MITM from extracting the shared secret used for the HMAC from the client-side validation code, and using that in the MITM attack?
If you just can't do realtime SSL crypto, could you reorganize your process so that you're using HTTP to deliver pre-computed responses which were GPG-signed, or otherwise signed offline as part of a batch process? If you need to include data from the user in computing/determining the response, could you add requests to a batch-processed queue, with GPG-signed results delivered later via the user polling an HTML response page, and/or via E-mail?
Do you need this to be available 24x7x365, or is this used sporadically? (e.g., registration -type events where you have a lot of load on one day or week, then nothing for months) If it's the latter, could you use a cloud-based server you just rent for a day or a week then spin down until you need it again?
Instead of trying to solve the wrong problem, grab an encryption acceleration card or put Nginx in front of your web server in an SSL accelerator/reverse proxy configuration and be done with it.