Client-side BCrypt, store salt and hash separately in MySQL database
The client is the attacker. Walk around your office while chanting that sentence 144 times; be sure to punctuate your diction with a small drum. That way, you will remember it.
In your server, you are sending Java code to run on the client. The honest client will run your code. Nobody forces the attacker to do so as well; and you use client authentication precisely because you fear that the client maybe be somebody else, who tries to impersonate the normal user. From your server point of view, you only see the bytes that you send to the client, and the bytes that come back. You cannot make sure that these bytes were computed with your code. The attacker, being an evil scoundrel, is perfectly able to envision not running your code and instead send the answer that your server expects.
Consider now a simple password authentication scheme where you simply store the users' passwords in your database. To authenticate, ask the user to send the password, and then compare it with what you stored. This is very simple. This is also very bad: it is called plaintext passwords. The problem is that any simple read-only glimpse at the database (be it a SQL injection attack, a stolen backup tape, a retrieved old harddisk from a dumpster...) will give the attacker all the passwords. To state things plainly, the mistake here is in storing in the database exactly the values that, when sent from the client, grant access.
And your proposed scheme ? Exact same thing. You store in the database the hash value "as is". And when the client sends that exact value, access is granted. Of course, the honest client will send the value by hashing a password. But, let's face it: many attackers are not honest people.
Now there is value in doing part of the hashing on the client side. Indeed, good password hashing is an arms race, in which the hashing is done expensive on purpose. Offloading some of the work on clients can be a nice thing. It does not work as well when clients are feeble, e.g. smartphones with Java, or, even worse, Javascript (which is a completely different thing, despite the name similarity).
In that case, you would need to run bcrypt on the client, and store on the server not the bcrypt output, but the hash of the bcrypt output with some reasonable hash function (a fast one like SHA-256 would be fine). The processing of a password P would then be a bcrypt on the client, then a SHA-256 of the result, computed on the server. This will push most of the CPU expense on the client, and will be as secure as a raw bcrypt for what it is meant to do (see below).
I understand that you want to "encrypt" passwords (hashing is not encryption !) because you want to use plain HTTP. You do not like HTTPS for the same reason as everybody else, which is the dreaded SSL certificate. Paying 20 bucks a year for a SSL certificate would be akin to having your skin removed with a potato-peeler sprinkled with lemon juice.
Unfortunately, there is no escaping the peeler. As others have remarked, even if you have a rock solid authentication mechanism, raw HTTP is still unprotected and an active attacker can simply wait for a user to authenticate, and hijack the connection from that point. A very common example of "active attacker" are people who simply run a fake WiFi access point -- that is, a completely real WiFi access point, but they also keep the option to hijack connections at any point. This is a kind of attack model that cannot be countered without a comprehensive cryptographic protocol that extends over all the data, not just an initial authentication method. About the simplest kind of such protocol is SSL/TLS. Any protocol that provides the same guarantees, that you absolutely need, will also be as complex as SSL/TLS, and much harder to implement because, contrary to SSL/TLS, it is not already implemented in the client software.
SSL is there, just use it. As for the lemon, suck it up.
(If the financial cost is a barrier, there are free alternatives, such as Let's Encrypt and pki.io. Whether they fit your bill remains to be seen, but they are worth considering if you are really short on cash.)
If you are not using a secured connection (https) then your schema is flawed in several ways:
- The hashed password is all that is needed to log in: you are vulnerable to a replay attack;
- Since the connection is not encrypted, the javascript you send to the client can be manipulated by the attacker (the attacker could just let the client send the password back to them).
If you want secure login, you need to be able to guarantee the client side code is correct and that there is nothing of value to be sniffed on the wire.
In almost all circumstances, the best solution for this is to use a SSL/TLS secured connection. You should send the non-hashed password over this secure connection and do the hashing on the server side.
Also, by removing the $2a$10$
part, you prevent yourself from increasing the iteration count anywhere in the future: because you do not store the iteration count, you have no method of identifying old hashes from new ones after you decide to increase the iteration count.
(If you can guarantee the client code, you could theoretically use SRP but getting it right is a complex affair and you are likely to fail).
If you are sending anything over an unencrypted HTTP connection, assume it is being watched and can be compromised with MITM.
If someone were to MITM you, they could supply their own salt and respond with their own hash. Therefore any attacker could completely spoof your authentication process.
You should be hashing the password server side and using a secure connection.