I have a WPF application with two PasswordBoxes, one for the password and another for the password to be entered a second time for confirmation purposes. I was wanting to use PasswordBox.SecurePassword
to get the SecureString
of the password, but I need to be able to compare the contents of the two PasswordBoxes to ensure equality before I accept the password. However, two identical SecureStrings are not considered equal:
var secString1 = new SecureString();
var secString2 = new SecureString();
foreach (char c in "testing")
{
secString1.AppendChar(c);
secString2.AppendChar(c);
}
Assert.AreEqual(secString1, secString2); // This fails
I was thinking comparing the Password
property of the PasswordBoxes would defeat the point of accessing only SecurePassword
because I'd be reading the plain-text password. What should I do to compare the two passwords without sacrificing security?
Edit: based on this question, I'm checking out this blog post about "using the Marshal class to convert the SecureString to ANSI or Unicode or a BSTR", then maybe I can compare those.
It looks like you could use this to compare the two
SecureStrings
.It uses unsafe code to iterate through the strings:
I have modified it below to work without unsafe code (note however you are able to see the string in plain text when debugging):
This doesn't have unsafe blocks and won't display the password in plaintext:
Edit: Fixed the leak as recommended by Alex J.
You could take a different approach. I hit the same problem in my code, the comparison of a password and a confirmation, both of type SecureString. I realised that the end goal was that the new password needs to be stored in the database as a base-64 string. So what I did was simply pass the confirmation string through the same code as if I were going to write it to the database. Then, when I have two base-64 strings, I compare them at that point, which is a simple string comparison.
It does take a bit more plumbing to communicate any failure all the way back to the UI layer, but the end result seemed acceptable. This code hopefully is enough to give the basic idea.
I am a bit of a newbie at security programming, so I welcome any suggestions for improvement.
Translating @NikolaNovák answer to plain PowerShell:
If the code is running on Windows Vista or higher, here is a version that's based on the CompareStringOrdinal Windows function, so there's no plain text, all buffers stay unmanaged. Bonus is it supports case-insensitive comparison.