Jump to content

Recommended Posts

I read about 15 other threads. None quite close enough.

 

I'm trying to validate a password for containing at least one character from three classes: uppercase, lowercase, number. All other characters can be a variety of things (A-Za-z0-9!@#$%^&*._-). The whole thing should be 6-32 characters.

 

I tried to do this myself, but I'm not up on this lookahead stuff. At least, I'm pretty sure I need to use lookaheads. I've been using this online tester for writing the expressions and testing them with sample inputs. I cannot get ANYTHING to validate.

 

I would love to understand lookaheads and how to check for a character class within the input in addition to getting this to work. So if you're up for it, I'm game for a teaching moment here as well.

 

My current iteration of trying to figure this out:

 

/^((?=[A-Za-z0-9]+)(?=[A-Za-z0-9!@#\$%\^&\*\._-]*)){6,32}$/

 

Any ideas? Thanks.

Edited by charlieholder
Link to comment
https://forums.phpfreaks.com/topic/273119-yet-another-password-regex/
Share on other sites

I don't limit passwords at all aside from minimum length/complexity limits.  Let a user enter whatever characters they want for however long they want to.  Once you hash the password with your algorithm of choice it doesn't really matter what their original input was.

 

As for the complexity rule you want (one upper, lower, and numeric) do three separate simple tests rather than trying to come up with a complex regex:

$pass = 'This is my pa55w0rd!';
$valid = 
   preg_match('/[A-Z]/', $pass)
   && preg_match('/[a-z]/', $pass)
   && preg_match('/[0-9]/', $pass)

 

Here's a simple RegExp for setting a minimum password complexity of 8 characters, with at least one digit, one lower case letter and one upper case letter:

$RegExp = '/^(?=.{8,})(?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).*\\z/';

 

To explain what it does:

^       # Bind the RegExp to the beginning of the string.
(?=.{8,})   # Followed by 8 or more characters (min length)
(?=.*\\d)   # At least one digit.
(?=.*[a-z]) # At least one lower-case character.
(?=.*[A-Z]) # At least one upper-case character.
.*\\z       # Followed by any number of characters, before the end of the string.

 

Now, you might be wondering about the .* inside the lookaheads. Having it there allows the conditions to occur anywhere in the string, by allowing any number of characters in front, and not just for the first character.

Edited by Christian F.

That regex can be simplified. Since the lookaheads do not consume any characters, there is no need to put a lookahead at the beginning looking for 8 characters - simply do a length check at the end

$RegExp = '/(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).{8,}/';

 

However, in addition to checking for at least one of each character class that regex will allow any characters. If you want to restrict which characters are allowed as you may have been insinuating - then you will need to modify that length check to include all the required characters as well as put the begin/end bindings to ensure there are no errant characters.

 

$RegExp = '/^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z\\d!@#$%^&*._-]{8,}$/';

^		   # Bind the RegExp to the beginning of the string.
(?=.*[a-z]) # Anything, followed by at least one lowercase (non consuming)
(?=.*[A-Z]) # Anything, followed by at least one uppercase (non consuming)
(?=.*\\d)   # Anything, followed by at least one number (non consuming)
[a-zA-Z\\d!@#$%^&*._-]{8,} # All characters must be within this class and be 8+ in length
$		   # Bind the RegExp to the end of the string.

Good point on the length limit, Psycho. Thanks. :)

 

Though, like Kicken I don't agree with limiting the entropy (or the max length) of a password. All you do is make it simpler for any attackers to crack it. A lot simpler. When it's hashed it'll be treated as binary data anyway, so it doesn't even matter if the characters are printable or not[1]. ;)

That's why my RegExp allows any character.

 

[1] Well... Except for the fact that you've got to type those characters into the form, somehow.

In theory, I agree that you should not restrict what characters can be entered. But, in practice, there are legitimate reasons for doing so. If a user is normally on a PC with a keyboard setup that allows some "non-traditional" characters, that user may have problems logging when away from that computer - i.e. on the road. Similarly, and probably more importantly, many characters that can easily be entered on a PC are either difficult or impossible to enter from a mobile device.

For some reason my "Topics & Posts" notification settings were set to none and my timezone was +1 GMT when I woke up this morning. Very strange. I missed all the discussion last night. I was up too because I couldn't sleep ;(

 

Thank you for the line-by-line explanations of example code. I think I have a much better grasp on lookaheads now. Thanks for the comments about character classes and length too.

 

I'm going to work on this based on the example code and comments and see what comes out. I'll post back here sometime after work today.

I'm not great at regex, I just wanted to say don't put an upper limit on passwords. I use several that are way longer than 32.

 

Yeah. I guess it really doesn't matter to me how long it is. As long as the hash is the same in the end. Point taken.

 

Let a user enter whatever characters they want for however long they want to. Once you hash the password with your algorithm of choice it doesn't really matter what their original input was.

 

Makes sense. Why should I care what other characters they enter as long as I force them to use a few different character classes to make the password somewhat stronger.

 

(?=.*\\d) # At least one digit.

 

What's the difference between

(?=.*\\d)

and

(?=.*[0-9])

in the regex?

 

But, in practice, there are legitimate reasons for doing so.

 

Oooh! Very interesting! I've never thought about that before. That being said, if you know that your password characters aren't available on mobile phone or other devices, then... maybe you shouldn't use them? lol

 

So I've taken all your comments under advisement and decided to end with this:

 

^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9]).{6,64}$

 

What do you think?

Edited by charlieholder

What's the difference between

(?=.*\\d)

and

(?=.*[0-9])

in the regex?

Nothing. The \d is the character class for digits

 

Oooh! Very interesting! I've never thought about that before. That being said, if you know that your password characters aren't available on mobile phone or other devices, then... maybe you shouldn't use them? lol

 

Perhaps, but someone would only find that out after they already created their password on a PC and then later needed to log in on a mobile device. I'm not saying you should exclude characters only that there are some legitimate reasons for doing so.

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.