Preg_match for password always errors - php

I'm using this for a password that needs to be 8-20 chars long, only numbers, letters and !##$% symbols:
if (!preg_match('/^(?=.*\d)(?=.*[A-Za-z])[0-9A-Za-z!##$%]{8, 20}$/', $_POST['password'])) {
$errors[] = true;
$_SESSION['error'] .= '<div class="messages status_red">Password must be 8-20 long, A-Z, 0-9, !##$% only.</div>';
}
However no matter if I meet the criteria or not I still return an error.
I don't see any error_reporting messages either.
Any idea what could be the reason for this?

The problem is the space in {8, 20}, which keeps it from being recognized as a {min,max} quantifier. But I don't know why you're complicating things with positive lookahead etc; something as simple as this should do the trick:
preg_match('/^[A-Za-z0-9!##$%]{8,20}$/', $_POST['password'])
EDITED TO ADD: It would be better security practice to allow any character at all in the password. If they can type it, let them use it - and it's on you to be able to process it without running afoul of SQL injection or similar. (You don't store passwords in your database anyway, right? Right.)
If you want to require a certain amount of character diversity in order to encourage stronger passwords - for instance, require at least one each of letter, number, and neither - then you can do something like this. Here, the use of lookahead means the letter, number, and neither-of-the-above can occur in any order in the password:
preg_match('/(?=.*[A-Za-z])(?=.*[0-9])(?=.*[^a-zA-Z0-9])/', $_POST['password'])
You could try to get the length requirements into the regex, too, but I would just check the length separately - and here again, I would have a minimum, but no maximum. (Since passwords should never be stored, but only checksummed, longer passwords shouldn't incur any additional storage overhead. But if you do run into a situation where you need to limit the length for some reason, try to pick a limit in the hundreds-of-characters range rather than the tens-of-characters range.)

Don't put a space in {8, 20}. It should just be {8,20}.

Related

Password validation with Respect Validation

I've currently got a regular expression set up for validating my passwords:
/^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*\W).{6,}$/
This checks for at least 1 uppercase and lowercase character, 1 digit, 1 special character/non-word character and minimum of 6 characters.
I'm now porting all of my validation rules over to Respect Validation. I've got most of it set up nicely but having difficulty with password validation.
I'm aware of the regex method:
Validator::regex('/^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*\W).{6,}$/')->setName('Password');
But this returns an error message like this:
Password must validate against "/^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*\W).{6,}$/"
I'm wanting to chain methods together so each rule is set in the validation chain. So if it's a digit you are missing it returns something like "Password must contain at least 1 digit", if you're missing an uppercase character it returns "Password must contain at least 1 uppercase character" and so on.
I thought it might be done using the allOff method, whereby you can set individual rules that will pass globally if all inner rules pass.
As a test, I tried to detect if the string contains a digit and an alpha character like this:
Validator::allOf(Validator::digit(), Validator::alpha())->setName('Password');
The results:
$password = 'test'; // Password must contain only digits (0-9)
$password = '1234'; // Password must contain only letters (a-z)
$password = 'test1234' // Password must contain only digits (0-9)
Looking at it now, it makes perfect sense why I got those errors, but I can't see how else to do this without the regex method. Is what I'm after possible is it just best to use the regex method in this case, I've gone through the rule list a few times but I've probably missed the one I'm after as there is quite a few.
Any help is appreciated.

Preg replace numbers if more than 5 digits

I have an image upload form. After user submitted the form, my script will process the image and clean the image filename (im appending a unique number series at the end of the filename to prevent possible duplicate filename.
Often Im receiving filenames (after processing) such as
"c-id-1333-l-id-1298491-aid-3951-id-13995346097186883-im-193-1.jpg"
How can I preg_replace the numbers if its more than 5 digits, if less than 5 digit or less it will be retain. The above example should give "c-id-1333-l-id--aid-3951-id--im-193-1.jpg" (Dont mind the multiple consecutive dash[-], my script can handle this.
You can use the following to do this.
$str = 'c-id-1333-l-id-1298491-aid-3951-id-13995346097186883-im-193-1.jpg';
$str = preg_replace('/\d{5,}/', '', $str);
var_dump($str);
Explanation:
\d{5,} # digits (0-9) (at least 5 times)
Output:
string(41) "c-id-1333-l-id--aid-3951-id--im-193-1.jpg"
If you want to retain 5 digits or less than 5 then you can use \d{6,} instead.
Is the user supplied name important?
If it is not, one technique i like to do to normalize file names in that case is to simply hash them with something like sha1 or even md5. Then add your timestamp, and ids or what not to that, this takes care of a lot of issues with special characters such as ".", "\" and "/" ( dot dot dash and directory traversal ) in the file names.
#hwnd's regular expression should do the trick, but I though I would throw this out there.
so with hashing you'd get cleaner ( but less meaningful ) names like this
da39a3ee5e6b4b0d3255bfef95601890afd80709.jpg
then you can add your unique numbers on
da39a3ee5e6b4b0d3255bfef95601890afd80709-1234568764564558.jpg
you could even salt the filename with the timestamp first and then hash them to get filenames all 40 characters long, and the chance of hash collisions is very minimal unless your dealing with 10's of thousands of files, in which case just up the hashing to sha256 etc.

how to check a password's Content and length using an array Functions

A user enters a password, say 'tomorrow1234'. I'm aware that I can split it into an array with str_split, but after that, I want to go through each value and search them for things such as capitalization, number, or white space.
How would I go about doing this?
This is an old standby function I use to valiate password complexity. It requires that the password contains upper and lowercase letters, as well as non-alpha characters. Length checks are trivial and are handled elsewhere.
$req_regex = array(
'/[A-Z]/', //uppercase
'/[a-z]/', //lowercase
'/[^A-Za-z]/' //non-alpha
);
foreach($req_regex as $regex) {
if( !preg_match($regex, $password) ) {
return NULL;
}
}
I use the array and a loop so it's easy to add/remove conditions if necessary.
Sounds like your trying to verify password strength.
Check out this web page, your solution would be pretty complex to write a specific answer for, but you can use regex to check for things like capitalization, symbols and digits. This page has several examples you could modify for your needs.
http://www.cafewebmaster.com/check-password-strength-safety-php-and-regex
This is what I would use:
(?=^.{8,}$)((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$
Checks for 1 letter, 1 number, 1 special character and at least 8 characters long.

PHP - preg_match()

Alright, so I want the user to be able to enter every character from A-Z and every number from 0-9, but I don't want them entering "special characters".
Code:
if (preg_match("/^[a-zA-Z0-9]$/", $user_name)) {
#Stuff
}
How is it possible for it to check all of the characters given, and then check if those were matched? I've tried preg_match_all(), but I didn't honestly understand much of it.
Like if a user entered "FaiL65Mal", I want it to allow it and move on. But if they enter "Fail{]^7(,", I want it to appear with an error.
You just need a quantifier in your regex:
Zero or more characters *:
/^[a-zA-Z0-9]*$/
One or more characters +:
/^[a-zA-Z0-9]+$/
Your regex as is will only match a string with exactly one character that is either a letter or number. You want one of the above options for zero or more or one or more, depending on if you want to allow or reject the empty string.
Your regular expression needs to be changed to
/^[a-zA-Z0-9]{1,8}$/
For usernames between 1 and 8 characters. Just adjust the 8 to the appropriate number and perhaps the 1.
Currently your expression matches one character
Please keep in mid that preg_match() and other preg_*() functions aren't reliable because they return either 0 or false on fail, so a simple if won't throw on error.
Consider using T-Regx:
if (pattern(('^[a-zA-Z0-9]{1,8}$')->matches($input))
{
// Matches! :)
}

Make sure username is not a phone number

I'm writing a mobile website and I would like the user to be able to login via username or phone number. I think the easist way to validate their response it to not allow them to signup using a phone number as their user name.
The problem is that I'll need to check if the input of the username field is JUST a 10 or 11 digit number. This is where my inexperance in regex comes to my disadvantage. I'm hoping to try something like
function do_reg($text, $regex)
{
if (preg_match($regex, $text)) {
return TRUE;
}
else {
return FALSE;
}
}
$username = $_POST['username'];
if(do_reg($username, '[0-9]{10,11}')){
die('cannot use a 10 or 11 digit number as a username');
}
The above regex is matching all numbers that are 10-11 digits long. I think maybe I need a way to say if the ONLY thing in the user input field is a 10-11 digit number get mad otherwise release the butterflies and rainbows.
EDIT: For the record I decided to make sure the username wasn't JUST a number. Thought this would be simpler and I didn't like the idea of having people use numbers as logins.
So I ended up with
if (!empty($username) && preg_match('/^\d+$/', $username )) {
die('username cannot be a number');
}
Thanks for the help all.
You are almost correct, except PCRE in PHP requires delimiters, and probably some anchors to make sure the field consists only of numbers.
if(do_reg($username, '/^\d{10,11}$/')){
// ^^ ^^
And probably use \d instead of [0-9].
(BTW, you should just call preg_match directly:
if (!preg_match('/^\d{10,11}$/', $username)) {
release('bufferflies', 'rainbows');
}
You need to anchor the regex to match the entire string: ^[0-9]{10,11}$.
^ matches the beginning of a string; $ matches the end.
Limit usernames to only 10 characters and require there username to start with a letter. How would a user write a 10 digit phone number as their username if they are required to enter in at least 1 alpha character (since phone numbers can't start with a 0/o or a 1/l)? (Heck I would require at least 3 alpha chars just to be safe).
When your app gets bigger then you can allow for longer usernames and take into account some of these issues:
Do not use ^ or $ signs if you are only testing the username: if(do_reg($username, '/^\d{10,11}$/')){
The reason I say this is anyone could defeat that by placing a letter in their username, a1235551212
instead use this:
if(do_reg($username, '/\d{10,11}/')){ because that will flag a1235551212d
Also, importantly, remember, that all of these regular expressions are only checking for numbers, there's nothing to stop a user from doing the following: ltwo3for5six7890. Unless of course you limit the username size.
You just should include start and end of the string in the regex
^[0-9]{10,11}$

Categories