2

I want to create a template for password entries and the conditions are:

  1. Password must be at least 12 characters long;
  2. There must be at least one lower case, one upper case, one number, and one special character;
  3. Specific special characters such as <>`"'~ are not allowed;
  4. Order does not matter, as long as the 3 previous conditions are met.

I have utilized negative lookahead to create a pattern, but that doesn't work. My pattern:

^(.{0,11}|[^0-9]*|[^A-Z]*|[^a-z]*|[a-zA-Z0-9<>~`|"\']*|[^\s]*\s.*)$

OS: Ubuntu 20.04

3
  • 7
    If you have any influence on these requirements, please push back against them. Forbidding some special characters in passwords is a sign that something is broken in your infrastructure: either it can be exploited by something other than a password, or passwords are being passed around to places that can't keep them secret. Requiring special characters in passwords is widespread, but misguided advice that pushes people to choose weaker passwords. security.stackexchange.com/q/131056 security.stackexchange.com/questions/16455 security.stackexchange.com/questions/6095 Commented Dec 3, 2021 at 11:48
  • 2
    Even just the fact that you're doing password validation in bash is a red flag. It's possible, but hard to verify that the password won't leak due to a bug in another part of the script, or that it won't leak through a side channel in the password validation code, or that it won't end up in a log somewhere. Commented Dec 3, 2021 at 11:50
  • 2
    @Gilles'SO-stopbeingevil' fully agree with your suggestion, but the bug in question is in Wordpress which doesn't accept these characters in the password when passed through Wp-CLI. Commented Dec 3, 2021 at 13:58

1 Answer 1

9

Your question does not mention how you try to use your expression. Regular expressions in the bash shell are POSIX extended regular expressions. These do not support positive or negative lookaheads or look-behinds.

Instead of using your expression to try to fit something more into it, I'm suggesting breaking the test down into several separate tests.

It's easiest to understand the implementation and to keep it maintained if each test is done independently, without trying to craft a monster expression to do it all.

#!/bin/bash

pw_is_valid () {
        local avoid='[<>`"'"'"'~]'

        [ "${#1}" -ge 12 ] &&
        [[ $1 =~ [[:upper:]] ]] &&
        [[ $1 =~ [[:lower:]] ]] &&
        [[ $1 =~ [[:digit:]] ]] &&
        [[ $1 =~ [[:punct:]] ]] &&
        [[ ! $1 =~ $avoid ]]
}

until
        IFS= read -r -s -p 'Enter password: ' pw
        pw_is_valid "$pw"
do
        echo 'Try again' >&2
done

I'm using a variable, avoid, to hold the regular expression [<>`"'~] to reduce the quoting needed to represent it.

In the POSIX aka C locale, the POSIX [:punct:] character class contains the characters in the following set:

!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~

In regular locales, it would typically contain a lot more¹.

I'm using it here as a stand-in for "special characters".


¹ for example, on Ubuntu 20.04 and in the en_GB.UTF-8 locale, it includes 147495 different characters. YMMV depending on the system, version thereof and locale.

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.