111

I'm setting up a home HTTP server which can send and receive JSON data to/from different clients (Android and iPhone apps).

I'd like to allow access only to certain users and I'm considering using a simple username/password mechanism, as setting up client certificates seems a bit of an overkill for this small project.

Of course I can't send clear passwords from the client to the server on plain HTTP, otherwise anyone with wireshark/tcpdump installed could read it. So, I'm thinking about the following mechanism:

  1. The HTTP server can be set up as HTTPS server
  2. The server also has username/password database (passwords might be saved with bcrypt)
  3. The client opens the HTTPS connection, it authenticates the server (so a server certificate is needed) and after exchanging the master key, the connection should be encrypted.
  4. The client sends the username/password in clear to the server
  5. The server runs bcrypt on the password and compares it with the one stored in the database

Is there any problem with this kind of configuration? The password should be safe since it's sent on an encrypted connection.

3
  • 6
    Just to add to the answers you've received already, in this kind of set-up I'd recommend looking at Certificate Pinning which helps mitigate MITM attacks... Aug 4, 2014 at 15:12
  • Maybe consider hashing the password instead of (or in addition to) encrypting it.
    – user541686
    Aug 7, 2014 at 2:53
  • 2
    Even though it might be safe if you use https. One problem which I read in other questions is that the username & password will get written to the server logs if it's directly passed in the urls. It can be dangerous if the logs security get compromised. Aug 25, 2017 at 3:00

9 Answers 9

85

Yes, this is the standard practice. Doing anything other than this offers minimal additional advantage, if any (and in some cases may harm the security). As long as you verify a valid SSL connection to the correct server, then the password is protected on the wire and can only be read by the server. You don't gain anything by disguising the password before sending it as the server can not trust the client.

The only way that the information could get lost anyway is if the SSL connection was compromised and if the SSL connection was somehow compromised, the "disguised" token would still be all that is needed to access the account, so it does no good to protect the password further. (It does arguably provide a slight protection if they have used the same password on multiple accounts, but if they are doing that, they aren't particularly security conscious to begin with.)

As MyFreeWeb pointed out, there are also some elaborate systems that can use a challenge response to ensure that the password is held by the client, but these are really elaborate and not widely used at all. They also still don't provide a whole lot of added advantage as they only protect the password from being compromised on an actively hacked server.

28
  • 6
    @AJHenderson +1, having the client send the hash would definitely be bad, as the point of the authentication protocol is to ensure that the visitor knows the correct password, not the correct password hash. If a malicious party could obtain the hash (stolen password database, man in the middle, whatever) and the system relied on the visitor sending the hash instead of the password, any notion of real security would be out the window. Aug 4, 2014 at 20:04
  • 9
    There is sometimes benefit to hash at client and then hash some more at the server. This can help ensure the password is obfuscated in any clear text logs at the client, server, and on the network.
    – Andy Boura
    Aug 5, 2014 at 7:41
  • 2
    @Craig Letting the client compute the expensive salted hash is perfectly fine, as long as you apply a cheap unsalted hash on the server before storing it (or some other kind of one way function, like modular exponentiation). Aug 5, 2014 at 12:29
  • 2
    @CodesInChaos - Because nothing client side is trusted. An attacker can skip the entire slow process by bypassing the client entirely. You need to make sure you can't build a rainbow table of cheap, fast hash values. It isn't feasible if you salt each and provided the intermediate hash is long enough. Otherwise, you just make a rainbow table of hash values which can be generated at a very, VERY fast rate if they are cheap. Aug 5, 2014 at 14:08
  • 2
    Also, while we're talking issues like POST vs GET, be sure to implement forward secrecy so your secure sessions can't be decrypted later on by somebody who obtains your server's private key. Aug 5, 2014 at 16:20
21

Not necessarily.

You also need to ensure the following:

  1. Your site is protected against cross-site request forgeries. Use Synchronizing Token Pattern.

  2. Your site is protected against session fixation attacks. Change session id on login.

  3. If using session cookies, that your entire site is HTTPS, not just the login URL, and that your session cookie is marked as secure and http only (no JavaScript access). Browser will send session cookie unencrypted if user types http://yoursecuresite (in same browser session).

  4. You are using a recent protocol. SSL 1 and 2 are broken, and 3 might be too. Try to use TLS 1.3.

  5. You are using a strong cipher.

  6. You are not using HTTP compression (GZip) or TLS compression. If your site displays user input (like a search input), then I can figure out your CSRF tokens and bank account number if you're using compression.

  7. Your server does not allow insecure client re-negotiation.

  8. You are using a 2048-bit RSA key (or the equivalent for an EC key), and that no one else knows your private key.

  9. You are using HSTS so browser goes direct to https even if user types http

  10. You are using perfect forward secrecy so your historical communications are secure even if your private key is leaked

10
  • if the entire site is https, do I Still need cookie marked as secure? They would still be encrypted "for free" due to the TLS layer, right?
    – Emiliano
    Aug 4, 2014 at 22:11
  • 2
    Can you explain point 6? Why compression would make the connection less secure?
    – Emiliano
    Aug 4, 2014 at 22:13
  • Ya you still want secure cookie, http only, as its still stealable if not. See xss. Compression kills encryption. See BEAST and CRIME attacks. Aug 4, 2014 at 23:17
  • 2
    @Emiliano: even your site is HTTPS only, an attacker can setup a man in the middle attack and setup a fake server that uses plain HTTP, performing what is known as SSL stripping. The browser will send to the fake server the user's cookie. To mitigate this, you need Secure cookie and HSTS policy.
    – Lie Ryan
    Aug 5, 2014 at 0:01
  • 9. Employ PFS to mitigate the risk of somebody (for example some evil government) stealing your private keys.
    – FooF
    Aug 5, 2014 at 12:12
16

As long as you verify the certificate validity, this is perfectly fine and is done all the time.

7

Most of the sites usually considered to be secure take pretty much the approach you are describing. Or put differently, you have simply described established industry standard.

I would recommend against using an approach less secure than the one you mention. (Whether bcrypt is better or worse than other salted hashes is a discussion I won't be going into. Just don't use anything weaker than a salted hash.)

If you want to distinguish yourself as having security above established industry standards, there are other options available. But it takes a huge effort in all areas of your application to make it worthwhile.

Areas regarding password validation that could be more secure include:

  • Protecting the server against DoS attacks by offloading most of the computation during validation to the client. I.e. don't iterate hashing on the server side, only iterate on client side and perform last step of hashing on server.
  • Protecting against password leaks if server is compromised by deriving a public key pair from the password and never let the server see password or secret key.
4

As others have said this is a standard approach.

However for a personal site I wouldn't necessarily follow it... I would use federated login from Facebook, Google or similar as that way I don't have to handle account life-cycle issues, and can use Google 2 factor Auth etc.

It saves having quite a few forms and fields in your database which means less to go wrong.

Of course you would still need to authorise those users you wish to be able to access either through a function of the authentication provider such as a Facebook group, some sort of whitelisting of allowed users, or an approval work flow off your account. Sometimes this is done by inviting users: giving them a URL containing a unique secure code and the your system linking that to an Auth provider on first login. Alternatively users authneticate and request access. This places them in a "pending" state. You then provide an interface where you can login and approve them.

4
  • 1
    Bit I don't want everyone to join: I want to give permission only to certain users (e.g. my friends).
    – Emiliano
    Aug 5, 2014 at 19:34
  • Yes, you need a list of emails that are allowed still. You then check that against the federated Id.
    – Andy Boura
    Aug 5, 2014 at 19:37
  • Ah, so I can implement a white list, you mean? That is interesting, I'll look into this approach as well.
    – Emiliano
    Aug 5, 2014 at 19:58
  • That's right. You could potentially even use your Facebook friends list or a group for Auth but I'm not familiar with details of how you would do it.
    – Andy Boura
    Aug 5, 2014 at 19:59
2

HTTPS makes the authentication request unsniffable in transit. However, to make it "safe", there are other things that you also need to get right. For example:

  • The entire login page and all of its dependencies should also have been served over HTTPS, even though no password is being transmitted then. Serving any part of it (such as JavaScript, CSS, or image resources) over unencrypted HTTP would let an attacker modify the appearance or behaviour of the login page through a man-in-the-middle attack.

    Browsers will treat mixed HTTP/HTTPS content with varying degrees of suspicion. Some will merely suppress the 🔐 "lock" icon in the UI. More paranoid browsers will refuse to load the unencrypted dependent resources altogether. Either way, you should serve the entire login page over HTTPS.

  • Do not submit the password in the query string of an HTTP GET. Webservers are typically configured to log the URLs of requests, which would include the query string portion of the URL. Put the password in a POST body instead. (You could also use RFC 2617 HTTP authentication, but logout support is spotty.)

1
  • Ah yes, I always remember GET is a bad idea because it is visible on the client system and may end up in client history, but forget that servers may log the parameters too. Aug 5, 2014 at 16:41
1

Wikipedia that has the full page of historical HTTPS security issues.

HTTPS security bugs have happened before (why the two older versions are broken? And what is 'goto fail'? What is 'https freak'?).

I do not suggest to ditch HTTPS, but placing something additional underneath will make no harm in most cases. If you already can send unencrypted password through that channel, you can probably send whatever you want.

0

The use of HTTPS does not prevent attempts to brute force the password. So if you are rolling your own user account management, then you might want to consider features such as minimum password complexity and account lockout / maximum number of retries that would mitigate against such attempts.

0

It is quite safe but you should consider hashing the password also on the mobile app (on android/ios) before you send it to the server. This client side hashing however cannot replace server side hashing so best would be to hash on both client and server side. From the server perspective this password hashed on the client side becomes the real password so you still need to hash it on the server side. However from the user's perspective it is safer because even the server does not know what is the original password.

The reason to hash on the client side is that this way the server will never even receive an original password in plaintext which is good. In case someone hacks into the server and you do not hash the password on the client side then he will receive plaintext passwords and users might use those passwords also in other places/websites. So by hashing also on the client side you are protecting the user. This is especially true in your case with a mobile app because attacker who hacks into the server cannot replace the code of your mobile ios/android app. In case of a web app if he hacks to the server he could also remove the client side hashing from the javascript on your website so in that case with a web app this client side hashing might have less sense. However still it can prevent for example saving the plaintext passwords accidentally in server logs. Beside that if the attacker needs to replace the code on the server to get plaintext passwords then it means that he needs to reveal his presence on the server which could be more easily noticed by the server administrator or by other users. So even for a web app there are some advantages here.

Think how many times you heard that somebody hacked into a server? Probably many times and in such case even if you have a proper password hashing on the server side the attacker still have access to raw user passwords during their login process. Also even some larger companies such as GitHub or Twitter admitted that they accidentally logged plaintext user passwords in their server logs which would never happen if they used client side hashing in addition to server side hashing.

If you hash on the server side with a random salt (which you should do anyway) then on the mobile app side you could hash the password concatenated with any unique constant string (for example domain string or just any constant long string with random characters) which should be easy to implement and does not need any special handling on the server side. You could also concatenate username to that password and constant domain string and calculate client side hash from that.

20
  • 3
    Hashing client side is useless. It does not really matter whether the server receives an original password or a derivated string: whatever it receives is what it will use to authenticate the user. In your system, if attackers manages to access the client side hashed password, they will not even try to extract the original password, because they can just reuse that hash. Jul 27, 2021 at 14:11
  • @SergeBallesta it seems that you did not even read my whole answer. I wrote that you should hash on BOTH client and server side and explained why so your comment seems to be not related to my answer because if server is also hashing the hashed password then there is no problem. Jul 27, 2021 at 14:13
  • @LeszekSzary no, he read it, he even mentioned "a derivated string". The comment is not incorrect and relates directly to your answer.
    – schroeder
    Jul 27, 2021 at 14:14
  • 1
    @LeszekSzary: Only 2 ways exists: either the secret is what the server receives and it can store a hash in a non invertible way, or the server sends a nonce to the client that computes a non invertible hash using that nonce and the secret and sends that hash back. But then the server has to store the secret in clear text (or at least with a reversible encryption). Client side hashing secures the communication over an insecure channel at the price of the server knowing the plain text secret. Jul 27, 2021 at 14:20
  • 1
    Well, if you ignore all the things we've mentioned, then sure, there's no reason not to. Please check out all the discussions around this topic here on this site. Please also check out what the actual best practices are when considering client-side hashing. And, the main point I've been making all along, it does not secure the scenario posed by the question. TLS is a safe way to pass a password. You have a "good idea" but it doesn't secure the system or process involved, and you do not account for the ways that it can legitimately be done.
    – schroeder
    Jul 27, 2021 at 20:30

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .