2

I got into a discussion with one of our senior developers over password hashing/transmission best practices and it left me curious as to what the industry standard best practice is.

In the previous/current revision of our web service, we transmit users' plaintext passwords with SSL to the server and hash them server side via SHA-256 prior to inserting them into the database. However, a security audit by a third-party company declared that this was insecure and not a good practice.

So for our future release, we've transitioned to using cryptojs to hash passwords (unsalted) client-side, and these values are stored directly into the database without any further manipulation.

The general consensus is that in the event of an internal security breach, we NEVER handle any user's plaintext password, and we would much rather someone bypass our security implementations and have a hashed password rather than a user's plaintext password.

I'm curious as to whether this is adequate or should we be performing a secondary salted hash server-side to further protect ourselves?

WhiteWinterWolf
  • 19,082
  • 4
  • 58
  • 104
JD Davis
  • 121
  • 5
  • 1
    The question 'Why is client-side hashing of a password so uncommon?' could be of relevance for you: http://security.stackexchange.com/questions/53594/why-is-client-side-hashing-of-a-password-so-uncommon – DaniEll Sep 23 '15 at 13:29
  • So your security threat is "hacker gets root on web server"? – Neil McGuigan Sep 23 '15 at 18:18
  • Their primary concern is never divulging a user's plain text password, not even to our internal network, staff, database, etc (in the event of a rogue agent). Their secondary concern is someone gaining access to a user's account. There is nothing incredibly sensitive in the database. – JD Davis Sep 23 '15 at 19:55

1 Answers1

6

First and foremost "best practice" is to cease to use the term "encryption" because SHA-256 is not encryption. This is called hashing. If your senior developers and the third-party auditors all talk of SHA-256 as "encryption" then rest assured that none of them actually knows what they are talking about.

In your system, whatever the client sends, be it "encrypted" (hashed) or not on the client, is a value that grants access; that value is thus password-equivalent and revealing it is as deadly to the security of your server as the plaintext password. That kind of client-side hashing does not bring the security improvement that your auditors appear to believe it to do. Compounding the situation is the fact that when the client performs hashing with Javascript code, that Javascript code was just sent by the server itself, so a compromised server could perfectly send some Javascript that simply receives the user password as he types it in, and send that password to a server in Mongolia.

In any case, human minds being what they are, user-remembered passwords are weak against brute force (called "dictionary attacks"). A simple invocation of a cryptographic hash function such as SHA-256, irrespective of how cryptographically secure that function is, is way too fast to provide much protection here; an off-the-shelf desktop PC can hash potential passwords at rates counted in billions per second. Good password hashing requires dedicated functions with a configurable (but inherently high) computational cost. You won't be able to do that properly in Javascript because Javascript is feeble for computation-intensive tasks.


The best practice is to use SSL (always), transmit the password as-is in the SSL tunnel, and let the server apply a proper password hashing function, i.e. bcrypt (with a high enough iteration count -- as high as can be tolerated given the available hardware and expected peak load).

Occasionally, one encounters auditors in want of something to say, and you may have to perform some ritual dancing to appease them. One of such dances is sprinkling cryptography in various places, like cake icing. This is idiotic but harmless, as long as you do not remove the important parts. In your case, you can do SHA-256 hashing on the client side, but you must not remove the proper password hashing on the server side -- that is, consider the client-computed hash result as the "password" and use that as input to bcrypt. (Beware that hash outputs are binary, not text, so some encoding is needed, e.g. hexadecimal or Base64.)

The extra SHA-256 would then be useless, but will not make your system weaker -- contrary to what you are purporting to do:

So for our future release, we've transitioned to using cryptojs to encrypt passwords (unsalted) client-side, and these values are stored directly into the database without any further manipulation.

If you do that, then the database contents can be used to grant immediate access to the server. A simple read-only glimpse at the database (e.g. a discarded broken hard drive, a lost backup tape, a SQL injection attack...), and your security has gone down the drain.

Tom Leek
  • 168,808
  • 28
  • 337
  • 475
  • I misspoke when I said encryption and definitely meant hashing. However, from talking to one of our senior devs, it seems their overall mentality is that they would much rather have an attacker gain access to a user's account (which can be rolled back) instead of giving them the user's password (which could be used on other sites). Do you possibly have any sources I can show them to demonstrate the necessity of server-side hashing, in conjunction with our client-side hashing? – JD Davis Sep 23 '15 at 13:45
  • Normally, the expression "password-equivalent" should be convincing enough. If what the client browser sends grants access, and that value is exactly what you store in the database, then your database contents are full of access-granting tokens that can be simply used. The crucial point to understand is that you send Javascript to the attacker's browser, but nothing forces the attacker to run _your_ Javascript. – Tom Leek Sep 23 '15 at 13:47
  • I think the developers agree that it's not the "best" method, but I was flat out told that they were waiting for concrete answers from either the auditors or our security team as to what the industry best practice is prior to them changing anything. Our whole platform is currently being rebuilt to move to azure, and when I peered through the source, this was the biggest glaring security hole that I spotted. So I was simply looking for some form of documentation I could bring to them to try to mitigate this danger. – JD Davis Sep 23 '15 at 13:50
  • @TomLeek Is there a difference between hashing the encoded base64 string vs decoding the sha-256 output and hashing that byte string? Would knowing the keyspace/length of a password not make the scheme weaker (or stronger, since it is superior to an average user's password)? – Gray Sep 23 '15 at 14:30
  • 1
    @Gray: using the binary output of SHA-256 incurs the risk of the bcrypt implementation to interpret the bytes as a string and stop at the first byte of value 0. **IF** your bcrypt implementation can process arbitrary binary inputs with bytes of value 0, then using the raw SHA-256 output should be exactly as secure as using the Base64-encoded SHA-256 output (not stronger, not weaker). – Tom Leek Sep 23 '15 at 14:50
  • @Jdsfighter - Re: `demonstrate the necessity` - google `online sha256 cracker` and then pipe the hashed passwords into one and show the ?senior? devs how insecure a sha256 hash is. – Neil Smithline Sep 23 '15 at 15:44