Debianuser
Debianuser

Reputation: 181

Bash regex for strong password

How can I use the following regex in a BASH script?

(?=^.{8,255}$)((?=.*\d)(?!.*\s)(?=.*[A-Z])(?=.*[a-z]))^.*

I need to check the user input(password) for the following:

  1. at least one Capital Letter.
  2. at least one number.
  3. at least one small letter.
  4. and the password should be between 8 and 255 characters long.

Upvotes: 4

Views: 6609

Answers (5)

Dennis Williamson
Dennis Williamson

Reputation: 360325

If your version of grep has the -P option it supports PCRE (Perl-Compatible Regular Expressions.

grep -P '(?=^.{8,255}$)(?=^[^\s]*$)(?=.*\d)(?=.*[A-Z])(?=.*[a-z])'

I had to change your expression to reject spaces since it always failed. The extra set of parentheses didn't seem necessary. I left off the ^.* at the end since that always matches and you're really only needing the boolean result like this:

while ! echo "$password" | grep -P ...
do
    read -r -s -p "Please enter a password: " password
done

Upvotes: 3

karatedog
karatedog

Reputation: 2618

These matches are connected with the logical AND operator, which means the only good match is when all of them match. Therefore the simplest way is to match those conditions chained, with the previous result piped into the next expression. Then if any of the matches fail, the entire expression fails:

$echo "tEsTstr1ng" | egrep "^.{8,255}"| egrep "[ABCDEFGHIJKLMNOPQRSTUVWXYZ]"| egrep "[abcdefghijklmnopqrstuvwxyz"] | egrep "[0-9]"

I manually entered all characters instead of "[A-Z]" and "[a-z]" because different system locales might substitute them as [aAbBcC..., which is two conditions in one match and we need to check for both conditions.

As shell script:

#!/bin/sh
a="tEsTstr1ng"
b=`echo $a | egrep "^.{8,255}" | \
             egrep "[ABCDEFGHIJKLMNOPQRSTUVWXYZ]" | \
             egrep "[abcdefghijklmnopqrstuvwxyz"] | \
             egrep "[0-9]"`
# now featuring W in the alphabet string
#if the result string is empty, one of the conditions has failed
if [ -z $b ]
  then
    echo "Conditions do not match"
  else
    echo "Conditions match"
fi

Upvotes: 1

Unreason
Unreason

Reputation: 12704

I get that you are looking for regex, but have you consider doing it through PAM module?

There might be other interesting modules.

Upvotes: 0

Arkku
Arkku

Reputation: 42149

I'm don't think that your regular expression is the best (or correct?) way to check the things on your list (hint: I'd check the length independently of the other conditions), but to answer the question about using it in Bash: use the return value of grep -Eq, e.g.:

if echo "$candidate_password" | grep -Eq "$strong_pw_regex"; then
    echo strong
else
    echo weak
fi

Alternatively in Bash 3 and later you can use the =~ operator:

if [[ "$candidate_password" =~ "$strong_pw_regex" ]]; then
    …
fi

The regexp syntax of grep -E or Bash does not necessarily support all the things you are using in your example, but it is possible to check your requirements with either. But if you want fancier regular expressions, you'll probably need to substitute something like Ruby or Perl for Bash.

As for modifying your regular expression, check the length with Bash (${#candidate_password} gives you the length of the string in the variable candidate_password) and then use a simple syntax with no lookahead. You could even check all three conditions with separate regular expressions for simplicity.

Upvotes: 1

codaddict
codaddict

Reputation: 455282

grep with -E option uses the Extended regular expression(ERE)
From this documentation ERE does not support look ahead.

So you can use Perl for this as:

perl -ne 'exit 1 if(/(?=^.{8,255}$)((?=.*\\d)(?=.*[A-Z])(?=.*[a-z])|(?=.*\\d)(?=.*[^A-Za-z0-9])(?=.*[a-z])|(?=.*[^A-Za-z0-9])(?=.*[A-Z])(?=.*[a-z])|(?=.*\\d)(?=.*[A-Z])(?=.*[^A-Za-z0-9]))^.*/);exit 0;'

Ideone Link

Upvotes: 0

Related Questions