20

Using a regex in Python, how can I verify that a user's password is:

  • At least 8 characters
  • Must be restricted to, though does not specifically require any of:
    • uppercase letters: A-Z
    • lowercase letters: a-z
    • numbers: 0-9
    • any of the special characters: @#$%^&+=

Note, all the letter/number/special chars are optional. I only want to verify that the password is at least 8 chars in length and is restricted to a letter/number/special char. It's up to the user to pick a stronger / weaker password if they so choose. So far what I have is:

import re
pattern = "^.*(?=.{8,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=]).*$"
password = raw_input("Enter string to test: ")
result = re.findall(pattern, password)
if (result):
    print "Valid password"
else:
    print "Password not valid"
jCuga
  • 1,523
  • 3
  • 16
  • 28
  • 4
    I recommend NOT using a regular expression for this test. it is much easier to maintain it that way. – Hamish Grubijan Jun 07 '10 at 15:14
  • 5
    Actually, the regex for this isn't complicated at all and makes quite a lot of sense. – Amber Jun 07 '10 at 15:15
  • 1
    Amber's answer is correct, but although a password verifier that allows `aaaaaaaa` may be "verified", allowing such weak passwords "if they so choose" isn't much better than a verifier that accepts `a`. Of note, one of the all time user-chosen favorite passwords is `password`; if you allow that, you might as well just skip passwords altogether. – msw Jun 07 '10 at 15:24
  • 1
    @Amber, the point of not using the regex is not because regexes are bad but because maintaining regexes is not easy and because password requirements are the kinds of things that change frequently. His example is "easy" for a regex as your answers shows. But as soon as someone says there must be at least 3 out of these 4 character set: at least 1 upper, at least 1 lower, at least 1 number and at least 1 special char, the complexity goes up. – jmucchiello Jun 07 '10 at 15:38
  • @jCuga, do you need this verification function be insanely fast? – Hamish Grubijan Jun 07 '10 at 15:40
  • 1
    @jmucchiello - Maintaining regexes is only complicated if you take the approach that everything must be done in a single regex, no matter how complex it is. The "intelligent" approach is to use regexes (because they *are* efficient ways of doing many tests) but not to try to lump too much into the same regex. For instance in your example, those "at least 1" tests can be done in *additional* code, they don't have to be added to the same regex. – Amber Jun 08 '10 at 02:47

8 Answers8

36
import re
password = raw_input("Enter string to test: ")
if re.fullmatch(r'[A-Za-z0-9@#$%^&+=]{8,}', password):
    # match
else:
    # no match

The {8,} means "at least 8". The .fullmatch function requires the entire string to match the entire regex, not just a portion.

Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
Amber
  • 507,862
  • 82
  • 626
  • 550
  • Ok, I have to admit that this is readable, and if it is not meant to change, then it is a good solution. A comment like "regex will not always be appropriate if business logic changes" might help, but then again - maintainers aren't dumb - you have to make that assumption, right? – Hamish Grubijan Jun 07 '10 at 15:38
  • @Hamish- I don't. Even if the maintainer is myself. Someone in the future will be assigned a change to that code and he will consider updating the regex as the "only" way to proceed for some period of time until he either comes up with some Rube Goldberg regex that works or he notices he's taking 3 days to make a quick change and finally ditches the regex structure. – jmucchiello Jun 07 '10 at 16:22
  • 2
    Well, I recently joined a company and fixed bugs for a few months. I've had a number of `WTF?` moments, which made me ask others questions and learn stuff. The usual explanation is: we wrote this 4/6/8/10 years ago, and this is how it was being done back then. I feel like since I passed the test of learning the messy system through fixing bugs, others should too. If a junior coder is having an easy time, then [s]he is either too smart for the group, or there is no learning. If you always work with hygienic code, then your "immune system" becomes whacked and/or starts to attack friendly code. – Hamish Grubijan Jun 07 '10 at 17:10
  • 1
    Amber you are absolutely correct and it seems like many of the comments are coming from people who don't understand how much you can do with regular expressions. For them I recommend this book: [Mastering Regular Expressions](http://www.amazon.com/Mastering-Regular-Expressions-Jeffrey-Friedl-ebook/dp/B007I8S1X0/ref=sr_1_1?s=digital-text&ie=UTF8&qid=1392214836&sr=1-1&keywords=mastering+regular+expressions) It will make them a better programmer. – p1l0t Feb 12 '14 at 14:22
  • This doesn't work as expected for me. The password comes up as a match even if it doesn't include a symbol. – Kajsa Dec 15 '21 at 13:06
  • 1
    @Kajsa the question specifically states it is not required to contain a symbol. – Amber Dec 16 '21 at 03:16
5

I agree with Hammish. Do not use a regex for this. Use discrete functions for each and every test and then call them in sequence. Next year when you want to require at least 2 Upper and 2 Lower case letters in the password you will not be happy with trying to modify that regex.

Another reason for this is to allow user configuration. Suppose you sell you program to someone who wants 12 character passwords. It's easier to modify a single function to handle system parameters than it is to modify a regex.

// pseudo-code
Bool PwdCheckLength(String pwd)
{
    Int minLen = getSystemParameter("MinPwdLen");
    return pwd.len() < minlen;
}
jmucchiello
  • 18,754
  • 7
  • 41
  • 61
3

Well, here is my non-regex solution (still needs some work):

#TODO: the initialization below is incomplete
hardCodedSetOfAllowedCharacters = set(c for c in '0123456789a...zA...Z~!@#$%^&*()_+')
def getPassword():
    password = raw_input("Enter string to test: ").strip()
    if (len(password) < 8):
        raise AppropriateError("password is too short")
    if any(passChar not in hardCodedSetOfAllowedCharacters for passChar in password):
        raise AppropriateError("password contains illegal characters")
    return password
Hamish Grubijan
  • 10,562
  • 23
  • 99
  • 147
  • 2
    You could use the `string` module to make the creation of your `hardCodedSetOfAllowedCharacters` easier/more readable: `set(string.letters + string.digits + '@#$%^&+=')`. Also, because set takes any iterable (and strings are iterable) you don't need the generator expression. – tgray Jun 07 '10 at 16:17
1
import re
password=raw_input("Please give me a password: ")
if len(re.findall("[A-Za-z0-9@#$%^&+=]",password))==len(password):
    print("Great password")
else:
    print("Incorrect password")

If you want to run it in python 3.0 and above change raw_input with input.

Kwnstantinos Nikoloutsos
  • 1,832
  • 4
  • 18
  • 34
1
import re 

password = input("Enter Password")
True if (re.fullmatch(r'^[A-Za-z0-9@#$%^&+=]{8,}$', password)) else False

if however you wanted to make the second bits required, you could do the following:-

True if (re.fullmatch(r'^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.[@#$%^&+=])[A-Za-z0-9@#$%^&+=]{8,}$', password)) else False

Note: [0-9] in python is a subset of \d so you might want to stick to [0-9] read the following post https://stackoverflow.com/a/6479605/4170558

N Djel Okoye
  • 950
  • 12
  • 10
0

If you wan the program to continuously run until a correct password is inputted do the following.

import re

while True:
    print('Input a strong password.')
    password = input()
    if re.match(r'[A-Za-z0-9@#$%^&+=]{8,}', password):
        print('Very nice password. Much secure')
        break
    else:
        print('Not a valid password')
yarrsmash
  • 5
  • 3
0

This regex validate if the password:

  • uppercase letters: A-Z -lowercase letters: a-z
  • numbers: 0-9
  • any of the special characters: !@£$%^&*()_+={}?:~[]]+

    re.compile(r'^.*(?=.{8,10})(?=.*[a-zA-Z])(?=.*?[A-Z])(?=.*\d)[a-zA-Z0-9!@£$%^&*()_+={}?:~\[\]]+$')
    
serv-inc
  • 35,772
  • 9
  • 166
  • 188
Gregory
  • 6,514
  • 4
  • 28
  • 26
0

But what if the user enters another special characters out of your list? Should you include all existing special characters? Another solution may be search what we expect to be in the password separately:

  • numbers: 0-9
  • lowercase letters: a-z
  • uppercase letters: A-Z
  • any special characters
  • No white space
p = password

def is_valid(p):
    if (len(p) >= 8 and
        re.search(r'\d+', p) and
        re.search(r'[a-z]+', p) and
        re.search(r'[A-Z]+', p) and
        re.search(r'\W+', p) and not
        re.search(r'\s+', p)):
        return True
    else:
        return False```
Farshid
  • 17
  • 6