使用积极的 LookAhead 和 LookBehind 来屏蔽数据库连接字符串中的密码

Using Positive LookAhead and LookBehind To Mask Password In Database Connection String

我的 VB.NET 应用程序中有一个异常处理程序方法,它从上次发生的异常中检索详细信息,并将该信息通过电子邮件发送给我们的帮助台(我)进行诊断和故障排除。一个可能的异常实例是当应用程序试图在我们的一个数据库服务器上连接或执行某些 SQL 时。在这些情况下 - 特别是当异常是由于连接失败时 - 我想查看用于确保其格式正确的数据库连接字符串。

但是,数据库连接字符串通常包含用户的 ID 和密码,我想在字符串中屏蔽密码。然而,挑战在于我们连接到多种数据库类型(例如,PostgreSQL、MySQL、SQLite、MS Access 等),并且根据数据库的不同,可能是也可能不是连接字符串中的密码。此外,连接字符串格式因提供商而异。

非常不熟悉 RegEx 但是,使用 https://regexr.com/,我设法想出了以下似乎有效的 RegEx 模式 有时:

(?<=;?[Pp][Aa][Ss][Ss].*=)(.*?)(?=;)|$

示例连接字符串如下所示:

Host=SERVERNAME;Port=####;Database=DBNAME;Username=USERID;Password=MyPa$$Word;Integrated Security=False

上面的模式正确匹配字符串中的 MyPa$$Word 如果它明确按此顺序,但如果我将 Password key/value 对移动到更靠近连接字符串开头的位置,例如这个:

Host=SERVERNAME;Port=9999;Password=MyPa$$Word;Database=DBNAME;Username=USERID;Integrated Security=False

然后匹配 MyPa$$WordDBNAME USERID。如果我将它移到字符串的末尾:

Host=SERVERNAME;Port=9999;Database=DBNAME;Username=USERID;Integrated Security=False;Password=MyPa$$Word

该模式未找到 任何 匹配项。为了确保 key/value 与 space (Integrated Security=False) 不会混淆模式,我将其从字符串中删除并得到了相同的结果。

因为根据数据库类型、用户输入等,连接字符串可能以多种方式构建,我希望能够使用 RegEx 查找(不区分大小写)密码 key/value 配对连接字符串中的任何位置,仅提取实际密码值,并将其替换为某些内容(如 [HIDDEN])。我知道我可能只对整个连接字符串执行 String.Split(Convert.ToChar(";")) 并检查每个 key/value 对,但如果可能的话,我更愿意使用 RegEx 来执行此操作。

类似这样的方法可行:

((^|;)Password=)(.*?)(;|$)

密码将在 中,因此请确保在执行替换时忽略该捕获组。

单击此页面左侧的 "Code Generator" 以在 PHP 中查看它的运行情况。

https://regex101.com/r/gxztmy/1

VB.NET中,可以考虑

text = Regex.Replace(text, "(?<=(?<![^;])pass\w*=).*?(?=;[\w\s]+=|$)", "[HIDDEN]", RegexOptions.IgnoreCase)

text = Regex.Replace(text, "(?<![^;])(pass\w*=).*?(?=;[\w\s]+=|$)", "[HIDDEN]", RegexOptions.IgnoreCase)

C# 版本以备不时之需:

text = Regex.Replace(text, @"(?<=(?<![^;])pass\w*=).*?(?=;[\w\s]+=|$)", "[HIDDEN]", RegexOptions.IgnoreCase);
text = Regex.Replace(text, @"(?<![^;])(pass\w*=).*?(?=;[\w\s]+=|$)", "[HIDDEN]", RegexOptions.IgnoreCase);

看到一个lookbehind regex demo and the capturing group solution demo.

详情

  • (?i) - 不区分大小写的修饰符(或 RegexOptions.IgnoreCase
  • (?<=(?<![^;])pass\w*=) - 需要字符串 pass(前面有 , 或字符串位置的开头)+ 任何 0 个或多个单词字符后跟 = 紧邻当前位置的左侧
  • .*? - 除 LF 符号外的任何 0+ 个字符尽可能少
  • (?=;[\w\s]+=|$) - 字符串中紧跟 ;、1+ 个单词或空白字符和 = 或字符串结尾的位置。

在捕获组解决方案中,使用 </code> 占位符将捕获的子字符串放回结果中。</p> <p><strong>为什么是 <code>(?<![^;]) 而不是 (?<=^|;)?因为在未锚定的后视中的交替在性能方面是昂贵的。如果存在没有交替的等效模式,则最小化开销是有意义的。 (?<![^;]) 匹配任何位于字符串开头或以 ; 开头的位置,因此它应该是首选。但是,如果左侧上下文是一个多字符字符串,或者如果需要多行模式,则这种模式是不可能的。