2

I am trying to write a program that uses regular expressions to make sure the password string it is passed is strong. A strong password is defined as one that is at least eight characters long, contains both uppercase and lowercase characters, and has at least one digit. So far, I have the following code. I have spent hours tinkering the regular expression, however I cant make it so that it will pass. Each time I make a change, it seems a strong password gets marked as weak, or a weak as strong. Any idea how I can improve this?

import re


pass2Regex = re.compile(r'''
    \d*
    [a-zA-Z]*
    \d*
    [a-zA-Z]*
    ''',re.VERBOSE)


text = raw_input("enter your password\n")
if len(text) >= 8:
    search = pass2Regex.findall(text)
    if text in search:
        print "%s is a strong password" % (text)
    else:
        print "%s is a weak password" % (text)
else:
    print "%s is a weak password" % (text)

For example, right now if the password were "231242441", it would be marked as a strong password even though there are no letters. Furthermore, When I try a + instead of a * it will only accept passwords that start with a digit, etc..

Thanks for the help

Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
brianhalperin
  • 63
  • 1
  • 6
  • See here: http://stackoverflow.com/a/75149/3928184 – bjd2385 Jul 13 '16 at 20:36
  • 7
    Also, see [*Regex for Password Must be contain at least 8 characters, least 1 number and both lower and uppercase letters and special characters*](http://stackoverflow.com/questions/19605150/regex-for-password-must-be-contain-at-least-8-characters-least-1-number-and-bot). Remove the "special characters" conditions and you have your answer. – Wiktor Stribiżew Jul 13 '16 at 20:36
  • @WiktorStribiżew's suggestion is even more useful than mine – bjd2385 Jul 13 '16 at 20:38
  • Relevant regex from that answer: `^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$` Another way to write that last part is `[^\W_]{8,}` – 4castle Jul 13 '16 at 20:41
  • @WiktorStribiżew Although that code is pretty helpful, it's sort of confusing. I appreciate the answer, but any chance you mind helping me with my version of the code? No worries if not. – brianhalperin Jul 13 '16 at 20:46
  • Here is the regex for your logic: http://ideone.com/o6a4N7 – Wiktor Stribiżew Jul 13 '16 at 21:02
  • 1
    To be honest, your expression would even validate an empty string :) – Jan Jul 13 '16 at 21:09
  • Before trying to write a regex to check a "strong password", take the time to search and ask you what is a "strong password". – Casimir et Hippolyte Jul 13 '16 at 22:09
  • Please consider changing your title to "Python regex for strong password" or similar. It will help others with the same problem find your question easier than "Python regex pattern" – JS. Jul 13 '16 at 22:20

2 Answers2

1

You have all of python to work with, so don't limit yourself to a single regexp. Write one regexp for each condition that the password must satisfy. "Contains uppercase"? Trivial. "Lowercase"? Also trivial. "At least one digit"? No problem. "At least 8 characters long"? You don't even need a regexp.

You can cascade the tests, combine a series of tests with and, or put the tests in a list and apply them in one go like this:

tests = [ "[A-Z]", "[a-z]", r"\d", ".{8}" ]
if all(re.search(pat, text) for pat in tests):
    print("It's a strong password")

Save your hours of effort for the next step.

alexis
  • 48,685
  • 16
  • 101
  • 161
  • Why should he if he can have it all at once? – Jan Jul 13 '16 at 21:05
  • 1
    Because any single-regexp "solution", even yours, will be far less readable, verifiable or extensible (i.e., maintainable) than splitting it up. – alexis Jul 13 '16 at 21:11
  • 1
    I agree with you that most regex solutions suffer from not being written in verbose mode - however, if you **do add** comments (and this is what I usually do), I think they are often more straight-forward than looking up every function's parameter - after all it's a matter of taste, I guess :) – Jan Jul 13 '16 at 21:16
  • Verbose mode is nice, but you've still got a regexp where all the work is done in multiple overlapping lookaheads that don't even use non-greedy wildcards. Thanks but I'll pass. And comments are not verification-- they can be incorrect. – alexis Jul 13 '16 at 21:23
  • 2
    @Jan I love regex. But fewer people can write/maintain a regex than can understand/update/maintain Python string methods. If a simpler, clearer, more easily maintained way is available, choose it. – JS. Jul 13 '16 at 22:27
  • @Akshit, what makes you think this not an answer? The answer is "split it up into several regexps and you can have a very simple solution." – alexis Jul 13 '16 at 22:28
0

As mentioned by others, this is a typical use-case for lookarounds (lookaheads in this situation).
A lookahead looks ahead without consuming characters (this is an assertion).
Broken down to your needs:

A strong password is defined as one that is at least eight characters long, contains both uppercase and lowercase characters, and has at least one digit.

^                    # anchors the expression to the beginning
    (?=[^a-z]*[a-z]) # not lowercase, followed by lowercase
    (?=[^A-Z]*[A-Z]) # not UPPERCASE, followed by UPPERCASE
    (?=\D*\d)        # not a digit, followed by a digit
    .{8,}            # at least 8 characters long
$                    # anchor the expression to the end

See a demo on regex101.com.


So for your code snippet this would be:
import re
pass2Regex = re.compile(r"""
    ^                    # anchors the expression to the beginning
        (?=[^a-z]*[a-z]) # not lowercase, followed by lowercase
        (?=[^A-Z]*[A-Z]) # not UPPERCASE, followed by UPPERCASE
        (?=\D*\d)        # not a digit, followed by a digit
        .{8,}            # at least 8 characters long
    $                    # anchor the expression to the end
""", re.VERBOSE)

password = "WeaK1234"
password2 = "strong?"

if pass2Regex.search(password):
    print "Strong!"

if pass2Regex.search(password2):
    print "Strong!"
Jan
  • 42,290
  • 8
  • 54
  • 79