18

I'm trying to create a regular expression in C# that will replace the password of a connection string so that it is not shown when I display it on a page. The connection string password are somewhere in the string as PWD=password;

So far I have:

Regex.Replace(connStr, "PWD=.*;", "PWD=********");

This works to find the beginning of the pattern, but the problem is the wild card (.*) is also including the ; so the pattern is never terminated and the remainder of the string is replaced too. How can I say everthing but a ; in my RegEx?

Thanks.

2
  • 4
    Just curious - I'm sure you have a good reason - but why would you ever show even a partial connection string on your web page? Commented Dec 2, 2011 at 17:11
  • It's for an admin webapp. Admins can modify connection strings via the app, but I don't want them to see the passwords on the list page. Commented Dec 2, 2011 at 17:24

5 Answers 5

29

You don't need to use RegEx for this - .NET has the built-in SqlConnectionStringBuilder class which you can use to get values from the connection string and change them.

Example code:

string conString = "Server=myServerAddress;Database=myDataBase;Uid=myUsername;Pwd=myPassword;";
var builder = new SqlConnectionStringBuilder(conString);

builder.Password = "********";

Console.WriteLine(builder.ToString());
Sign up to request clarification or add additional context in comments.

5 Comments

I don't know Oracle, so I can't tell for sure. But I just googled a bit and found the OracleConnectionStringBuilder which looks like what you're looking for. It's deprecated and will be removed in the future, though...Microsoft suggests to use a 3rd party provider in the future.
Thanks. If I remeber this project (it's been a while) the connection string could have been for any DMBS, not just MSSQL. But thanks for the tip. Good to know.
Who says it is deprecated? Seems to be in the latest ODP.NET I have for 12c??\
"Deprecated" means "will be removed at some point". I don't remember the exact wording in the MSDN link that I posted >6 years ago, but now it redirects to learn.microsoft.com, and there it says "This type is deprecated and will be removed in a future version of the .NET Framework. For more information, see Oracle and ADO.NET."
Oh, and the second link in my comment directly redirects to the "Oracle and ADO.NET" page.
24

You can use a non-greedy quantifier:

PWD=.*?;

Or exclude ;s:

PWD=[^;]*;

7 Comments

That's what I was missing. Thanks!
In the event it's the last item and not trailed by a semicolon you should probably use: PWD=([^;]*)(?:$|;)
That's a good point. I also changed PWD= to (PWD=|pwd=) to make it case insensitve.
@Paul: That's not case insensitive. Passing RegexOptions.IgnoreCase is case insensitive :)
I used this variant... $"({key})=([^;]*)(?:$|;)"; so I can pass a connection string keyword to the regex and mask whatever e.g. var keys = new string[] { "PASSWORD", "PWD", "USER ID", "UID", "DATA SOURCE", "DSN" }; foreach (string key in keys) { pat = $"({key})=([^;]*)(?:$|;)";
|
1

Here is the regex I'm using

(?<=Password=).+?(?=(;|'|"|$))

With positiv look behind to find the start of the password, positiv look ahead to find the end of the Password and a not greedy match Expression. The "Detektion Tokens" (Password= ;'"$) might differ in varios scenarios.

Testcases: https://regex101.com/

c# implementation

using System.Text.RegularExpressions;
private static Regex _regex = new Regex("(?<=Password=).+?(?=(;|'|\"|$))");
//...
var decryptPwdMatch = _regex.Match("some connectionstring for example");
//...

Comments

0

We came across this issue and a colleague came up with the following regex:

"(?i)(?:^|.*;)pwd=(\".*\"|.*?)(?:;|$)+?"

or

"(?i)(?:^|.*;)password=(\".*\"|.*?)(?:;|$)+?"

if there is password in it instead of pwd.

The Password matches with the following code:

            var regex = new Regex(mask);
            var password = string.Empty;
            var match = regex.Match(connectionString);
            if (match.Success && match.Groups.Count > 1)
            {
                password = match.Groups[1].Value;
            }

2 Comments

Careful; if the password is in quotes and something else is in quotes too, .* is going to match as much as possible. It should probably be .*? too. Also, the (?:^|.*;) could just be (?<=^|;) — probably more efficient.
and you can just do (pwd=|password=) for 1 magic regex! But the best option is to use the ConnectionStringBuilder mentioned below...
0

Lots of problems with the suggested solution. This is not pure Regex, but handles casing, white spaces and optional trailing separators (, or ;)

public static class StringExtensions
{
    public static string MaskField(this string str, string field, string mask = "***")
    {
        var separators = ",;";
        var sb = new StringBuilder();

        foreach (var keyValue in Regex.Split(str, $"(?<=[{separators}])"))
        {
            var temp = keyValue;
            var index = keyValue.IndexOf("=");
            if (index > 0)
            {
                var key = keyValue.Substring(0, index);
                if (string.Compare(key.Trim(), field.Trim(), true) == 0)
                {
                    var end = separators.Contains(keyValue.Last()) ? keyValue.Last().ToString() : "";
                    temp = key + "=" + mask + end;
                }
            }

            sb.Append(temp);
        }

        return sb.ToString();
    }
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.