1

I need to validate passwords. I currently use: preg_match("/^[a-z0-9_-]*$/i", $pass).

I would like to add length to this. My mysql table is set up like this : userpassword varchar (40) NOT NULL,. So between 6 and 40 characters. And I would like to allow all characters that are not dangerous to put in my db.

ganjan
  • 7,356
  • 24
  • 82
  • 133
  • 4
    First thing that comes to my mind from reading your question is that you don't encrypt passwords... http://php.net/md5 – acm Jan 25 '11 at 17:18
  • 4
    @andre matos: MD5 is not an [encryption](http://en.wikipedia.org/wiki/Encryption), it’s a [cryptographic hash function](http://en.wikipedia.org/wiki/Cryptographic_hash_function). But you’re right: The password should not be stored in plain text. – Gumbo Jan 25 '11 at 17:24
  • 1
    Don't limit your passwords simply to alphanumeric characters. It's better to enforce a set of rules such as minimum of 3 upper-case, minimum of 3 lower-case, minimum of 2 numeric, minimum of 1 non-alphanumeric (alter rules to suit your personal preferences), but insisting on non-alpha does force the brute-force attacks to do more work. When your password is in the database, it should be a hashed (and salted) value anyway, so the actual characters are irrelevant to database storage – Mark Baker Jan 25 '11 at 17:29
  • What does the /i do? Can't find reference to it anywhere. – Barbs Mar 24 '13 at 07:20

5 Answers5

8

Imposing arbitrary complexity rules on passwords is very user hostile and does not improve security substantially. Don't do it.

Here's why I think the above statement is true:

  • If I want to use your website with "123456" as my password, it's my problem. Let me roll with it.
  • You may impose a minimum length (e.g. 6 characters), but not a maximum. If I want to cite the entire John Maynard as my password, let me roll with it.
  • Your idea of a "secure" password might not be everybody else's idea.
  • People might use password generators that do not automatically comply with your rule set. Don't annoy them by not accepting their password for no other reason than not containing enough/or too many "special characters".
  • You must hash your customer's passwords with a decent hashing algorithm plus a random hash salt, different for every user. Only store hash salts and hashes in the database, never store clear text passwords.
  • Once hashed, even a lame password is reasonably secure against theft/cracking. Implement security against brute-force attacks (time-based lock-outs, IP-based lock-outs, password locking with e-mail handshake to retrieve a locked account).

So your password validation process goes like this

  • New user? Create user record with username and random salt (never change the salt value for that user)
  • Returning user? Fetch salt from DB, re-hash his password, compare result to hash in DB.
  • Never store the user's password anywhere physically and use HTTPS to transmit it.
  • If you do not want to do something like the above, think about using OAuth with your site. May not be easy either, but you do not have to worry about password security anymore and your users have one less password to remember.

For the sake of the argument, this regex will do what you ask. If you're still desperate to do it.

preg_match("/^[a-z0-9_-]{6,40}$/i", $pass)
Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • 2
    +1 See also [Do Strong Web Passwords Accomplish Anything? (PDF)](http://www.usenix.org/event/hotsec07/tech/full_papers/florencio/florencio.pdf) – Gumbo Jan 25 '11 at 17:46
  • @SuperSpy: Only the password transfer needs to bee HTTPS, not the entire site. Anyway, there's still risk to have your session cookie stolen by tools like FireSheep if not all of your data transfer is encrypted. But not transmitting the password in clear text should be the least to do. – Tomalak Jan 25 '11 at 17:48
  • Please don't limit what I can use in my password. If I want to use a `&`, let me... – ircmaxell Jan 25 '11 at 17:53
  • 2
    @Tomalak: This is a good article about session security: http://jaspan.com/improved_persistent_login_cookie_best_practice – SuperSpy Jan 25 '11 at 17:53
  • You forgot to close the grouping bracket in your regex. :-) – CanSpice Jan 25 '11 at 18:39
  • @CanSpice: You are correct. I did even not mean to put one there in the first place, thanks for pointing this out. – Tomalak Jan 25 '11 at 18:56
2

You should validate your passwords by ensuring they are secure, not that they're insecure.

public function password_is_secure($password) {
  // quick obvious test
  if (is_numeric($password)) {
    // FAIL: 'Your password cannot be all numbers.'
  } elseif (strlen(count_chars($password, 3)) < 4) {
    // FAIL: 'Your password needs to have a variety of different characters.'
  }

  // levenshtein distance test (test similarity to common passwords)
  $name = $_POST['name'];
  $email = $_POST['email'];
  $badPasswords = array($name, $email, 'password', 'password1', 'password123', '1234abcd', 'abcd1234', '12345678', '1234567890');

  foreach($badPasswords as $bad) {
    if (levenshtein($password, $bad) < 6) {
      // FAIL: 'Your password is too similar to your name or email address or other common passwords.'
    }
  }

  return true;
}

Plug-in more appropriate "getters" for $name and $email and setup how you want to handle passing error messages, and the above method will do you some justice. You can "tune" it by altering the allowed Levenschtein distance (currently 6).

I would also recommend extending the list of $badPasswords to include a bunch of the most common passwords.

And for the love of some deity, salt and hash your passwords before you store them in the database.

coreyward
  • 77,547
  • 20
  • 137
  • 166
  • +1 for the "check against most common passwords" hint. I was hesitant of writing this because I said "let me roll with '123456'" in my answer, but it's probably not too much to forbid the really common ones. – Tomalak Jan 25 '11 at 17:59
  • The issue with numeric passwords is that users don't realize that they're exponentially easier to brute-force. At 6-digits, there are only 1M possibilities, vs 6 alphanumeric characters at 56 billion possibilities. At 8-digits it's at a still-insecure 100M possibilities, vs 8 alphanum chars for 217 trillion possibilities. – coreyward Jan 25 '11 at 21:18
  • 1
    Assuming the attacker *knows* that the user only uses numbers – Ward Muylaert Jan 26 '11 at 13:49
  • Anybody worth their salt doing a brute-force attack (especially against a table of users) is going to start with the area with the highest probability of successful results. If they're not using a dictionary, it's all about the numbers. – coreyward Jan 27 '11 at 00:40
2

I would like to give you to the following tips:

OpenID, Facebook Connect

you should not be storing passwords in your database. Use OpenID via the really easy LightOpenID and for the following reasons:

  • When you use an openid provider then you DON'T store the passwords(incorrectly) so there can NOT be passwords stolen.
  • Your user do NOT have to create yet another account to login into your site.
  • The good OpenID providers have SSL in place so your passwords is NOT sent over the wire in plain-text.

Better not

P.S: for the fun of it. Somebody else asked me on stackoverflow.com if his loginsystem was any good/safe(it was NOT), so I wrote a SAFE login system. If you use SSL(and if I did NOT miss any vulnerabilities, but I do NOT believe I did).

length / safe

I would like to add length to this. My mysql table is set up like this : userpassword varchar (40) NOT NULL,. So between 6 and 40 characters. And I would like to allow all characters that are not dangerous to put in my db.

I do NOT like regexp because they are overly complicated(I only use as last effort). I would advise you just to use an up to date version of PHP(>5.2.0) which has protection in place to make it safe against XSS.

The filter extension is enabled by default as of PHP 5.2.0. Before this time an experimental PECL extension was used, however, the PECL version is no longer recommended or updated.

Next validating length is as simple as using strlen function.

Community
  • 1
  • 1
Alfred
  • 60,935
  • 33
  • 147
  • 186
  • OpenID is really not that great for users. That's why OAuth is so much more popular/common. And honestly, what's with all the shouting and giant headings? – coreyward Jan 25 '11 at 21:08
  • @coreyward you are oauth is for authorization and openid for authentication. They are completely different things. To make it stand out. I have 2 lines in heading. The rest is small. But I guess I could change it a little bit. – Alfred Jan 25 '11 at 21:40
  • "Facebook Platform uses the OAuth 2.0 protocol for authentication and authorization." — http://developers.facebook.com/docs/authentication/ Need I say any more? – coreyward Jan 25 '11 at 21:48
  • @coreyward I guess you don't need to say more :P, but the orginal problem oauth tackled was authorization. BTW LightOpenID is a really easy openID library. Which OAuth library can I use in PHP to do authentication as easily as LightOpenID. – Alfred Jan 25 '11 at 21:58
  • It doesn't matter that you can integrate OpenID easily, because your users are unlikely to use OpenID unless they're tech users. That's the point. – coreyward Jan 25 '11 at 22:57
  • @coreyward login in using google's openID does not require any effort at all! I think you should try to sample and tell me if it isn't easy to implement and use => http://gitorious.org/lightopenid/lightopenid/blobs/master/example-google.php. If users already have a google account they don't have to create yet another account. If not let them create an account with google which is going to be much safer then when you write an implementation yourself. – Alfred Jan 25 '11 at 23:04
  • It doesn't matter that you can integrate Google's OpenID easily, because your users are unlikely to use OpenID to sign in with their Google account unless they're tech users. That's the point. – coreyward Jan 25 '11 at 23:25
  • I think you are wrong about that! As also is Jeff Atwood author of stackoverflow.com agrees with me. P.S: it happens automatically when you redirect them to https://www.google.com/accounts/o8/id openid provider. I find your point about tech users kind of b*llshit! OpenID used to be hard in the past because you needed to know your unique ID, but with good widgets it isn't hard to do at all! – Alfred Jan 25 '11 at 23:55
1

You should hash the passwords, if your database gets cracked an attacker can read out all the passwords if you don't.

UPDATE: Check the post of Tomalak

      sha1($_POST['pass']); // will encrypt the password to a 40 character long string.
      echo(sha1('monkey')); // will become:          ab87d24bdc7452e55738deb5f868e1f16dea5ace
      echo(sha1('Monkey')); // completely different: 4bd0ec65b8f729d265faeba6fa933846d7c2d687
      // Just by making the letter upper-case!

If you want to login a user encrypt the pass from the login form and compare it to the database.

      $pass = 'monkey';
      if(sha1($pass)=='ab87d24bdc7452e55738deb5f868e1f16dea5ace'){
          echo('Correct pass!');
      } 

If you want to validate the length of a password, you can use this piece of code:

    if($upass=='' || $upass=='Password'){
        $errupass = 'Please fill in a password.';
        $valid = false;
    }elseif(strlen($upass)<6){
        $errupass = 'Password must be atleast 6 characters.';
        $valid = false;
    }

I shouldn't discourage people to use weird characters since they increase the strength of a password.

SuperSpy
  • 1,324
  • 3
  • 13
  • 28
  • 2
    Again: MD5 is not an encryption, it’a cryptographic hash function! Encryption implies that there is a inverse function, but there isn’t. Use “to hash” if you want to use a technical term. – Gumbo Jan 25 '11 at 17:33
0

Well, to start off with, you won't be putting a password in plain-text into your database, as you'll be cryptographically hashing it first. Thus you don't really care about which characters are or aren't dangerous to put into your database, as you'll get back a fixed-length alphanumeric string.

Your regex before hashing would be something like:

/^([a-zA-Z0-9]{6,40})$/

...placing whatever characters you want to allow in your passwords in the character class brackets.

Secondly, you don't really care about what characters you put into your database as you'll be using placeholders in your insert query, right?

CanSpice
  • 34,814
  • 10
  • 72
  • 86