1

Question: How can I write this without putting in all the possible cases? If someone gets 4 password requirements one day it will be easy to put in but be very difficult to debug since there will be 4! different ways to work the middle.

How I have it in code:

[RegularExpression("^[A-Za-z0-9!@#$]*([A-Za-z][0-9][!@#$]|[0-9][!@#$][A-Za-z]|[!@#$][A-Za-z][0-9]|[!@#$][0-9][A-Za-z]|[0-9][A-Za-z][!@#$]|[A-Za-z][!@#$][0-9])[A-Za-z0-9!@#$]*$", ErrorMessage = "The Password value must contain at least one letter, one digit, and one special character.")]

For viewing convenience I broke it so no scrolling is required:

[RegularExpression("^[A-Za-z0-9!@#$]*
([A-Za-z][0-9][!@#$]|[0-9][!@#$][A-Za-z]|
[!@#$][A-Za-z][0-9]|[!@#$][0-9][A-Za-z]|
[0-9][A-Za-z][!@#$]|[A-Za-z][!@#$][0-9])
[A-Za-z0-9!@#$]*$",
ErrorMessage = "The Password value must contain at least one letter,
one digit, and one special character.")]

As you can see this is 3! possibilities from the middle () separated by | (OR) so from this you can see 4! possibilities would be very difficult to maintain.

Question Requirements: The basic thing I want this RegularExpression in C# to do is require (in no particular order) at least one letter, at least one number, at least one special character !, @, #, or $

johnatrik
  • 33
  • 1
  • 6
  • 1
    Trying to improve readability of regular expressions is not the way to go here. Wrap your password checks into meaningfully named classes/methods that will hide your from the high level view of your code. – LeffeBrune Jul 31 '14 at 19:23
  • [Check out this page](http://stackoverflow.com/questions/25063854/regex-password-validation-or-operation/25064024#25064024) and tell if that helps. The regex in the question should be the one you're looking for. If not, try to clarify your question – skamazin Jul 31 '14 at 19:25
  • By the way, password policies like this are pretty much universally dumb: http://xkcd.com/936/ And I agree though you CAN do this with a regular expression, trying to make the regex more readable is not the way to go. Treat it like a policy with rules, and verify the rules one by one. You can than tell the user which of the dumb policy rules were not adhered to. With a regular expression it is more difficult to do this. – Keith Jul 31 '14 at 19:30
  • I am currently doing this via client-side by jQuery where I can say "Hey, your password is missing at least one {letter|number|special character}." Now I am trying to use model validation making the most of MVC framework without relying so much on jQuery. I checked out the comic you linked, but this is an implemented validation check I am just trying to move into model validation instead of jQuery with alert messages. – johnatrik Jul 31 '14 at 19:40

1 Answers1

1

One Regex

You can use zero-width positive lookahead assertions (see MSDN's "Regular Expression Language - Quick Reference" - in the Grouping Constructs section) to check for your ANDed password requirements - without having to explicitly cover all the orders in which the required password components may occur:

^(?=.*?[a-zA-Z])(?=.*?[0-9])(?=.*?[!@#$]).{3,}$

This works as follows:

  • (?=.*?[a-zA-Z]) - an alpha character found ahead
  • (?=.*?[0-9]) - and a number found ahead
  • (?=.*?[!@#$]) - and an allowed special character ahead
  • .{3,} - three or more characters in total

I have shared a regex fiddle based on this that you might find useful.

Property-Per-Criterion Password Checker

Per @LeffeBrune's comment, you should consider alternatively checking with a dedicated password-checker class that exposes a property per criterion.

For example, here is a quick & dirty PoC in LINQPad 4...

void Main()
{
    var passwords = new string[] {"password", "passw0rd", "passw0rd!", "123456", "!#@$"};

    foreach (var pw in passwords)
    {
        var checker = new PasswordChecker(pw);
        var isValid = checker.IsValid;

        Console.WriteLine("Password {0} is {1}valid.", pw, isValid ? "" : "in");

        if (!isValid)
        {
            Console.WriteLine("    Has alpha?  {0}", checker.HasAlpha ? "Yes." : "NO!");
            Console.WriteLine("    Has number?  {0}", checker.HasNumber ? "Yes." : "NO!");
            Console.WriteLine("    Has special?  {0}", checker.HasSpecial ? "Yes." : "NO!");
            Console.WriteLine("    Has length?  {0}", checker.HasLength ? "Yes." : "NO!");
        }
    }
}

public class PasswordChecker
{
    public const int MINIMUM_LENGTH = 3;
    private string _password;

    public PasswordChecker(string password)
    {
        _password = password;
    }

    public bool HasAlpha
    {
        get
        {
            return Regex.Match(_password, "(?=.*?[a-zA-Z])").Success;
        }
    }

    public bool HasNumber
    {
        get
        {
            return Regex.Match(_password, "(?=.*?[0-9])").Success;
        }
    }

    public bool HasSpecial
    {
        get
        {
            return Regex.Match(_password, "(?=.*?[!@#$])").Success;
        }
    }

    public bool HasLength
    {
        get
        {
            return _password.Length >= MINIMUM_LENGTH;
        }
    }

    public bool IsValid
    {
        get
        {
            return HasLength && HasAlpha && HasNumber && HasSpecial;
        }
    }
}

...that produces the following output:

Password password is invalid.
    Has alpha?  Yes.
    Has number?  NO!
    Has special?  NO!
    Has length?  Yes.
Password passw0rd is invalid.
    Has alpha?  Yes.
    Has number?  Yes.
    Has special?  NO!
    Has length?  Yes.
Password passw0rd! is valid.
Password 123456 is invalid.
    Has alpha?  NO!
    Has number?  Yes.
    Has special?  NO!
    Has length?  Yes.
Password !#@$ is invalid.
    Has alpha?  NO!
    Has number?  NO!
    Has special?  Yes.
    Has length?  Yes.

You could take this quick & dirty PoC much further of course, but the benefits of knowing what criterion or criteria failed are hopefully clear.

J0e3gan
  • 8,740
  • 10
  • 53
  • 80
  • 1
    Great! This does work. I was doing a similar RegularExpression but I left off the .{3,} part which when adding it worked. Mine looks exactly like yours except I don't have a + after [!@#$], is that really needed? – johnatrik Jul 31 '14 at 19:53
  • @johnatrik: Good catch on the lingering `+` after `[!@#$]`. In the breakdown I listed, I did not have it; but I left it in the complete pattern, which is not what I intended. No, it is not necessary, and I have removed it from the complete pattern and the related fiddle. – J0e3gan Aug 01 '14 at 06:24