#1
  1. Banned
    Devshed Expert (3500 - 3999 posts)

    Join Date
    Jul 2012
    Posts
    3,938
    Rep Power
    0

    User authentication: How (not) to do it


    User authentication: How (not) to do it

    Now that PHP 5.5 with its brand new password API is out, I thought it makes sense to recapitulate the whole issue of storing passwords and user authentication in general.

    While plaintext passwords seem to finally die out, there are far too many applications still relying on laughably weak algorithms like MD5 or SHA. This is an attempt of clearing things up a bit.

    Password security matters! If you store the passwords of your users, you’re not only responsible for your own application. You may, unknowingly, be responsible for countless Facebook accounts, Amazon credentials and mailboxes, because people do reuse their passwords, no matter how often they’ve been told not to.

    1. Avoid user authentication
    2. Things that do not work
    3. Things that work

    Comments on this post

    • badger_fruit agrees : Great post!
    • derplumo agrees : Always trying to better the internet;)
  2. #2
  3. Banned
    Devshed Expert (3500 - 3999 posts)

    Join Date
    Jul 2012
    Posts
    3,938
    Rep Power
    0
    Avoid user authentication

    This may sound strange, but it’s actually the first question you should ask yourself: Do I even need user accounts? Authenticating users massively increases the complexity of your application (if done correctly!), it’s a huge burden for both your users and yourself, and it comes with all kinds of problems. If you do it, you should have a very good reason for it.

    The most secure way of dealing with sensitive data is to not have it.
  4. #3
  5. Banned
    Devshed Expert (3500 - 3999 posts)

    Join Date
    Jul 2012
    Posts
    3,938
    Rep Power
    0
    Things that do not work

    Plaintext passwords

    You think this isn’t even worth mentioning, because nobody is stupid enough to put the actual passwords into the database? Well, Sony, Yahoo and Billabong did, and many others still do.
    If you store passwords as plaintext, you deliberately violate the security, privacy and integrity of your users. Don’t do it.



    MD5, SHA and friends (with or without salt)

    MD5 is probably the most misunderstood and misused security algorithm of all times. Every bad PHP tutorial out there uses it, and even major content management systems and message boards still go with MD5.

    If you’ve already heard that MD5 is “somehow insecure”, you’ve probably switched to the SHA family: SHA-1, SHA-256, SHA-512, the bigger the number, the better – or something like that. And maybe you’ve added a random salt to every password.

    Problem is, none of those algorithms was ever intended for password hashing, and they completely and utterly fail at this task. MD5 and SHA were designed for digital signatures. They’re supposed to be fast and not take up a lot of resources. When dealing with passwords, this is the worst possible property, because it allows anybody with an average gamer PC to calculate trillions(!) of hashes per second and “crack” passwords simply by brute force . Grinding through all alphanumerical combinations (lowercase and uppercase) up to six characters is a matter of minutes, regardless of whether it’s MD5, SHA-1, SHA-2 or whatever.

    Adding a salt also doesn’t help. While it prevents an attacker from breaking all hashes at once or simply looking them up at Google, it doesn’t change the fact that every particular hash can easily be attacked.

    So MD5 and SHA provide no security whatsoever for average passwords. They do work for extremely strong passwords, but those are rare.



    Basic Authentication and Digest Access Authentication

    Those are the ugly password prompts popping up when you visit a protected resource. With the rise of the REST paradigm, they’ve gained new popularity.

    Neither Basic Authentication nor Digest Access Authentication provide any relevant security. The official specifications actually point that out explicitly. While they may be fine to prevent average visitors from, say, seeing the photos of your last vacation, they’re completely unsuitable for anything more important.

    Basic Authentication is not insecure per se, because it’s merely a protocol for authenticating with a password. How the password should be store isn’t specified. In reality, however, all current implementations use weak algorithms like MD5, so it’s as insecure as any other MD5 approach.

    Digest Authentication is an obsolete protocol from the times when SSL/TLS wasn’t widely available. Since the authors assumed that the traffic between client and server is unencrypted, they came up with a protocol which doesn’t require the password to be actually transmitted to the server. However, the server must store the authentication data as plaintext, so this is effectively a plaintext password scheme.



    Self-made algorithms

    Surprisingly, many people try to come up with their own hash algorithms. They combine MD5 and SHA in all sorts of ways, they add “secret strings” and whatnot.

    This is a very bad idea. Unless you happen to be a professional cryptographer, you simply cannot tell how good your algorithm is. It could be anything from halfway secure to completely broken – with a strong tendency towards the latter.

    Designing a cryptographic algorithm isn’t as easy as it may look like. It requires expert knowledge, extensive peer reviews and tests, and the algorithm has to prove itself in real life for many years before it can be considered secure.

    A homemade algorithm simply cannot fulfill these requirements. In fact, most ideas laymen come up with are fundamentally broken and extremely insecure.

    So don’t even try to invent your own hashing scheme. Always go with established, widely accepted solutions. Leave cryptography to the cryptographers, just like you leave surgeries to actual doctors.



    Low-level functions

    Another common mistake is to fumble with low-level cryptography or use algorithms found somewhere on the Internet.

    This is a bad idea for the same reason why you shouldn’t invent your own algorithms: Cryptography is difficult and unforgiving. Even the slightest mistake can break the whole algorithm. Many programmers thought they could do it, and almost all of them failed. Some didn’t know how to generate secure random numbers, some got the math wrong, and some just made a tiny mistake somewhere.

    Don’t join that team.



    Encryption

    Encrypting the passwords with something like AES may sound like a good solution at first, but it’s not, and it shows a misunderstanding of what password storage is supposed to do.

    If you encrypt data, anybody who knows the key can decrypt it and retrieve the original input. So the security of the passwords depends entirely on you keeping the key secret. Make one mistake, and the passwords end up as plaintext. Even worse: You can’t just hide the key somewhere. It has to be on your server, readable by the application.

    This is a huge and totally unnecessary risk. When you set up an authentication system, you do not want to retrieve the passwords. Never. All you want to do is check the login data – and that’s what specialized hash algorithms are for.
  6. #4
  7. Banned
    Devshed Expert (3500 - 3999 posts)

    Join Date
    Jul 2012
    Posts
    3,938
    Rep Power
    0
    Things that work


    Medium security: Specialized password hash algorithms + HTTPS

    An algorithm for hashing passwords must fulfill specific requirements:

    • The algorithm must have a variable “cost factor” to make it computationally expensive for current hardware and slow down brute force attacks.
    • It must add a unique random string (a so called “salt”) to every password so that it’s impossible to attack all hashes at once or use precomputed hashes.

    There are currently three established algorithms which do this: bcrypt, PBKDF2 and scrypt.

    bcrypt is widely accepted as a secure algorithm, and it’s well-supported by PHP. This is what you should use unless you have specific reasons for choosing a different algorithm.

    As of PHP 5.5, bcrypt is available through the new password API:

    PHP Code:
    <?php

    $test_password 
    'pô?ŤVbÁ<pČ+r®U@ÚĽĄv/?PłRÎ+Í(´-p?';

    // hash password using bcrypt and a cost factor of 10
    $hash password_hash($test_passwordPASSWORD_BCRYPT, array('cost' => 10));
    // the result is a string with 60 characters containing the hash parameters (the algorithm and the cost factor), a random salt and the hash itself
    echo $hash'<br />';

    // check password
    if ( password_verify($test_password$hash) ) {
        echo 
    'Correct password.';
    } else {
        echo 
    'Wrong password.';
    }
    If you don’t have PHP 5.5 but at least 5.3.7, use the password_compat library, which emulates the new password API. If you don’t even have PHP 5.3.7, it’s time for an update, because you’re running abandoned software.

    PBKDF2 and scrypt are alternative algorithms. However, they aren’t well-supported by PHP, so they’re not really an option (unless you know exactly what you’re doing).

    No matter which algorithm you choose: You must use HTTPS at least for the login form and the login script (and every other form or script involving the password). Without HTTPS, anybody who happens to be between the user and your server is free to fetch the plaintext password and manipulate your response in any way they want. In this scenario, no hash algorithm can help you.

    You also need to be aware that even the strongest algorithm doesn’t make the hashes immune to attacks. If the passwords themselves are weak, it may very well be practical to attack them, even if they’re hashed with, say, bcrypt. A good hash algorithm can only buy you some time, nothing more.

    You must protect the password hashes in all circumstances!

    If they get leaked, consider them to be broken and immediately tell your users that they need to change the password everywhere they’re using it.

    You should also educate your users on password security: choosing strong passwords, using password managers etc.



    High security: SSL/TLS client authentication

    Password authentication is more or less acceptable for standard user accounts, but it’s actually pretty insecure and requires a lot of work to deal with its weaknesses.

    Most passwords are incredibly bad, and since they have to be stored on the server and transmitted for authentication, they’re constantly in danger. The only reason passwords are so popular is because they’re seemingly easy to handle, and better methods (namely public-key authentication) simply aren’t established in the general public yet.

    However, when dealing with privileged accounts like moderators or administrators, you can and should employ public-key authentication. With public-key authentication, users prove their identity by performing cryptographic operations which require a certain key. The key itself never leaves the client’s PC, so no sensitive data must be transmitted to the server or stored on the server.

    Public-key authentication can be done with plain SSL/TLS. Usually, only the server is authenticated: When you establish a SSL/TLS connection, the server sends its certificate, and then the browser checks if the certificate is valid and was issued by some trusted certification authority (CA). However, the server can also authenticate the client by asking for a valid client certificate and proof of ownership.

    The exact configuration depends on your webserver. I’m using nginx as an example, but the general logic is always the same. I assume you have OpenSSL on your server.

    Note: Please don’t use the code examples for anything but testing. This is just a quick demonstration.


    1. Create a key pair and a self-signed certificate for signing the client certificates.

    Instead of having a commercial certification authority like VeriSign sign the client certificates, we’ll create our own “mini CA”. In practice, you would do this on some offline machine not attached to any network.

    bash Code:
    # generate private key for our CA
    openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -aes-256-cbc -out client_ca.key
    # create self-signed certificate for the CA
    openssl req -new -key client_ca.key -sha256 -days 365 -out client_ca.csr
    openssl x509 -req -in client_ca.csr -signkey client_ca.key -sha256 -days 365 -out client_ca.crt

    If you’re running some old OpenSSL version, you’ll need to use the genrsa command instead of genpkey:

    bash Code:
    # generate private key for our CA
    openssl genrsa -des3 -out client_ca.key 2048
    # create self-signed certificate for the CA
    openssl req -new -key client_ca.key -sha256 -days 365 -out client_ca.crt
    openssl x509 -req -in client_ca.csr -signkey client_ca.key -sha256 -days 365 -out client_ca.crt


    2. Create a client certificate and sign it with the CA key.

    bash Code:
    # create private key for the client
    openssl genpkey -outform PEM -out client.key -aes-256-cbc -algorithm RSA -pkeyopt rsa_keygen_bits:2048
    # create certificate request for the client
    openssl req -sha256 -new -key client.key -out client.csr
    # create client certificate using the CA key
    openssl x509 -req -in client.csr -CA client_ca.crt -CAkey client_ca.key -sha256 -days 365 -set_serial 01 -out client.crt


    3. Import the client certificate into the browser.

    Browsers expect a PKCS #12 file, which contains both the client certificate and the corresponding private key.

    bash Code:
    # export the certificate and private key of the client into a PKCS #12 file
    openssl pkcs12 -export -in client.crt -inkey client.key -descert -out client.p12

    Import this file into your browser. Since it includes the unencrypted private key, you must protect it with the master password of your browser. If you haven’t set up a master password yet, do it now.

    For additional security, the client certificate can be kept on a smart card.


    4. Set up client authentication in the webserver configuration

    The webserver needs access to the CA certificate generated above (client_ca.crt). This will be used to check the client certificates.

    nginx
    Code:
    ssl_client_certificate  /usr/local/nginx/conf/client_ca.crt;
    ssl_verify_client       on;
    When you visit the protected resource, you should now be prompted for a client certificate. And you’ll only gain access after you’ve sent a valid certificate and proven that you own the private key.

    The authentication details can also be passed to PHP for further processing, since you’ll probably want to know the exact username of the moderator or administrator.
    Last edited by Jacques1; September 5th, 2013 at 06:46 PM.
  8. #5
  9. Banned
    Devshed Expert (3500 - 3999 posts)

    Join Date
    Jul 2012
    Posts
    3,938
    Rep Power
    0
    Wow, the feedback is gigantic.

    May I assume that you're simply not interested in security topics?
  10. #6
  11. Come play with me!
    Devshed Supreme Being (6500+ posts)

    Join Date
    Mar 2007
    Location
    Washington, USA
    Posts
    13,760
    Rep Power
    9397
    I didn't even see this existed.

    Originally Posted by Jacques1
    Basic Authentication and Digest Access Authentication
    However, the server must store the authentication data as plaintext, so [Digest authentication] is effectively a plaintext password scheme.
    You can store the hash of the user + realm + password to avoid that... but now you're tied to that realm so changing it could cause problems.

    Originally Posted by Jacques1
    Encryption
    ...
    It's possible for a boss to pull rank and insist that passwords must absolutely be recoverable (as opposed to resettable), but I've yet to hear a good reason for why that should be required that isn't due to misinformation, ignorance, or ego.

    For the "High Security" section, I'd call out the warning about not using the code samples even more. Bold or colored or whatever. People like me are prone to reading enough to see that the code is relevant to their issue, then jumping straight to the code.
  12. #7
  13. Banned
    Devshed Expert (3500 - 3999 posts)

    Join Date
    Jul 2012
    Posts
    3,938
    Rep Power
    0
    First of all: Thanks for the reply.



    Originally Posted by requinix
    You can store the hash of the user + realm + password to avoid that...
    This is the password. That's what you need in order to be authenticated. The original user-defined password doesn't really matter. It's just used to derive the "actual password".

    With Digest Authentication, the server must know the password (the actual one, not the original password), because it has to hash it together with the nonce. It cannot store it in an unrecoverable form. The best it could do is encrypt it.

    And I hope we agree that the original password isn't exactly safe either, given that we're talking about MD5 here. (It is secure it it's very strong, but this would make the choice of Digest Authentication even more absurd).

    Originally Posted by RFC 2617
    There are two important security consequences of this. First the
    password file must be protected as if it contained unencrypted
    passwords, because for the purpose of accessing documents in its
    realm, it effectively does.


    Originally Posted by requinix
    It's possible for a boss to pull rank and insist that passwords must absolutely be recoverable (as opposed to resettable), but I've yet to hear a good reason for why that should be required that isn't due to misinformation, ignorance, or ego.
    Well, and even if the boss has some excellent reasons for choosing an insecure scheme -- that doesn't change the fact that it's insecure.

    I'm only talking about facts, not politics. If your boss or customer or whoever is in charge wants you to store the passwords as plaintext, well, then you may just have to do that. But you wouldn't do it if you had a choice.



    Originally Posted by requinix
    For the "High Security" section, I'd call out the warning about not using the code samples even more. Bold or colored or whatever. People like me are prone to reading enough to see that the code is relevant to their issue, then jumping straight to the code.
    Sorry, but I fear my English skills are letting me down again.

    Are you suggesting not to use code samples, because they distract people from the text?
  14. #8
  15. Come play with me!
    Devshed Supreme Being (6500+ posts)

    Join Date
    Mar 2007
    Location
    Washington, USA
    Posts
    13,760
    Rep Power
    9397
    Originally Posted by Jacques1
    Sorry, but I fear my English skills are letting me down again.

    Are you suggesting not to use code samples, because they distract people from the text?
    Oh no, I just mean bolding or coloring the "don't use this code for really real because it's just to demo the idea" warning. Definitely keep the code there, but I know that I personally would go straight for the code and skip most of the explanation before it.

    Comments on this post

    • Jacques1 agrees
  16. #9
  17. Confused badger
    Devshed Beginner (1000 - 1499 posts)

    Join Date
    Mar 2009
    Location
    West Yorkshire
    Posts
    1,055
    Rep Power
    487
    Jacques1, thank you for a great thread/read.
    I'm not a security expert by any means but this is very thought provoking and have bookmarked it for future reference!
    "For if leisure and security were enjoyed by all alike, the great mass of human beings who are normally stupefied by poverty would become literate and would learn to think for themselves; and when once they had done this, they would sooner or later realise that the privileged minority had no function and they would sweep it away"
    - George Orwell, 1984
  18. #10
  19. Come play with me!
    Devshed Supreme Being (6500+ posts)

    Join Date
    Mar 2007
    Location
    Washington, USA
    Posts
    13,760
    Rep Power
    9397
    Originally Posted by requinix
    It's possible for a boss to pull rank and insist that passwords must absolutely be recoverable (as opposed to resettable), but I've yet to hear a good reason for why that should be required that isn't due to misinformation, ignorance, or ego.
    On that note, Adobe used password encryption for their user information... and it failed them. Turns out just throwing encryption at the problem of passwords isn't enough because you might do it poorly.

    One more reason against using encryption.
  20. #11
  21. Banned
    Devshed Expert (3500 - 3999 posts)

    Join Date
    Jul 2012
    Posts
    3,938
    Rep Power
    0
    There's even a crossword of the leaked Adobe passwords.

    Anyway, the underlying problem is not that they f*cked up the encryption. The problem is that they even kept the passwords. I wonder why. Would they send me my password if I asked for it?

    Can it get even worse? It can. The online shop of a big manufacturer of cryptographic smart cards (of all things) will send you the plaintext password in every f*cking email -- unencrypted, of course.

    I understand that people often don't wanna hear about the security problems of their code. But when you look at cases like this, you realize two things: Yes, people do break into servers and steal passwords. It's a real danger. And, no, not everybody is aware of that.
  22. #12
  23. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Aug 2013
    Location
    China
    Posts
    21
    Rep Power
    0
    I've been reading a few of your articles and the thread on secure logins. Very useful and eye-opening! Cheers.
  24. #13
  25. Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Feb 2013
    Posts
    311
    Rep Power
    8
    I really appreciate you sharing your knowledge with us, how do you know all this if I may ask?

IMN logo majestic logo threadwatch logo seochat tools logo