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,503
  • 3
  • 16
  • 27
  • 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

7 Answers7

32
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
  • 561,645
  • 34
  • 376
  • 476
Amber
  • 477,764
  • 81
  • 611
  • 541
  • 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,239
  • 7
  • 40
  • 61
  • One thing I am not sure about is: should the user be warned about just one type of error (invalid password), or all different kinds? Depends on the business logic, I suppose. – Hamish Grubijan Jun 07 '10 at 15:22
  • 1
    If he needs to change the password validity requirements later, it doesn't matter whether he used a regular expression or not *today*. It's just a function which returns `true` or `false` (at least it should be) and it doesn't matter whether it uses regular expressions or not. – Deniz Dogan Jun 07 '10 at 15:27
  • 1
    Depends on the audience. If the users are generally pre-validated (such as on a intranet app) you want to hold their hands and reduce support costs. In the wild, all this does is tell your potential attacker how to reduce the size of his dictionary. I think this is why you see those algorithms showing password strength on some web sites. They don't tell you what is wanted exactly but still enforce the rules in some way. The note to the user could just say "must be 60% or better". (Of course when those things are just javascript that doesn't stop the attacker from reading the code.) – jmucchiello Jun 07 '10 at 15:30
  • @Deniz - Maintenance of a bunch of little functions is infinitely easier than maintenance of a single complex regex. If you want to debate that, you live in a different programming world than I do. – jmucchiello Jun 07 '10 at 15:32
  • There are 10 kinds of people in this world: those who get Perl and those who do not ;) – Hamish Grubijan Jun 07 '10 at 15:36
  • @jmucchiello: I'm not sure what programming world you live in, but in the one I live in no one is afraid of regular expressions, especially not expressions as simple as this one. Should the validation logic get more complicated, he should rewrite it without depending on one single regular expression. I'm just saying that if the validation logic gets more complicated later, he will most likely still have to start over from scratch, independent of whether or not he used a regular expression to begin with. – Deniz Dogan Jun 07 '10 at 15:50
  • @Deniz - Why use something that would have to be rewritten completely in an area you just know the requirements will eventually change? Regexes are great in only two places: ad hoc throw away stuff (like command lines) and deep, lowlevel libraries where the requirements are as closed to fixed in stone as they can get. Anywhere in between is just asking for trouble down the road. Password validations are also wrong for regex because they almost never have order. Generally you want at least 1 upper and 1 lower but the order doesn't matter. As soon as you add a third at least 1 class.... – jmucchiello Jun 07 '10 at 16:17
  • ... the regex becomes overly complex. Since the field of password validations is generally wrong for regexes you should start with a regex just because the current requirements are simple enough for regex. Password requirements will change. It is nearly a fact. Why start with a tool you know you will need to replace once the complexity goes up? – jmucchiello Jun 07 '10 at 16:19
  • @jmucchiello: *IF* the requirements are this simple, then *yes*, he should go for a regular expression. *IF* the requirements are more complex, then he should go for something else. A programmer's job is *not* to predict future modifications to the original specification. – Deniz Dogan Jun 08 '10 at 15:25
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,278
  • 21
  • 94
  • 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,382
  • 4
  • 13
  • 28
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
  • 725
  • 8
  • 7
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
  • 32,612
  • 9
  • 143
  • 165
Gregory
  • 5,355
  • 4
  • 27
  • 26