How to secure passwords over HTTP?
You can't.
To securely send information over an unsecure channel, you need encryption.
Symmetric encryption is out, because you would first need to transport the key, which you can't do securely over an unsecure channel[*].
That leaves you with public key cryptography. You could of course roll your own, but you don't want to be a Dave, so that's out, which leaves you with HTTPS.
[*] You can of course try to use a secure channel to exchange the key, for example physical exchange of a key. Then you can use secret key crypto, like Kerberos.
TLS is really the only way to do it.
But what if I encrypt it with JavaScript?
The attacker can change the JavaScript you send to the client, or simply inject their own JavaScript that logs all information entered.
Then I'll use CSP and SRI to prevent the addition of scripts and the modification of my own scripts.
Glad you're using modern tools to help protect your site, but SRI in a non-secure context really only protects against the compromise of an external resource. An attacker can simply modify the CSP headers and SRI tags.
There's really no way to do this without using encryption from the beginning, and the only standard way to do that is to use TLS.
While I agree that you should use HTTPS, you can make it even more secure by using the Salted Challenge Response Authentication Mechanism (SCRAM, see RFC 5802) for the actual authentication exchange.
It's not trivial to implement, but the gist of it is that, rather than send the password to the server, you send proof to the server that you know the password. Since deriving the proof uses a nonce from the client and server, it's not something that can be reused by an attacker (like sending the hash itself would be).
This has a few added benefits to using HTTPS with the basic authentication you describe:
- The server never actually sees your password as you log in. If someone has managed to insert malicious code onto the server, they still won't know what your password is.
- If there is a bug in the HTTPS implementation (think Heartbleed), attackers still won't be able to acquire your password. Because, even once TLS is bypassed, the password isn't available.
- If, for some reason, you can't use HTTPS, this is better than sending the password in the clear. An attacker will not be able to guess your password based on the exchange. That said, you should still use HTTPS, because defense in depth is better.
Please note that this is only secure if the client is able to perform the SCRAM computations without downloading code from the server. You could provide a fat client, or users may be able to write their own code that they control. Downloading the SCRAM code over HTTP would give an attacker an opportunity to modify the SCRAM code to send the password in cleartext, or otherwise expose it.