可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
We have the following scenario:
- MVVM userinterface where a user can place his password (actually a
PasswordBox
)
- Server that shall do some work
- Server connects to some Database that requires authentification
And I already read this Question on PasswordBox in MVVM
But there is no answer on how to do! Just lots over "never ever do that".
What is the correct way of passing a password around?
How to resolve the security issues?
There is no proper way of Binding
to the PasswordBox
and
the Password shall not be stored somewhere, okay.
So, what is the MVVM way of doing such things?
Even if the pattern is broken, is there a good way to achieve such things?
Thought of a Func<string>
to retrieve it, but without Binding this
will get a mess...
Update
Same for initialising the PasswordBox from a (hopefully encrypted) password store.
Isn't that breaking the MVVM pattern? The User does not want to enter the password
each time he starts the application or wants to work with the database I believe.
回答1:
Personally I just pass the entire PasswordBox
control to my LoginCommand
I know it breaks MVVM because the ViewModel layer now references a View-specific object, but I think in this specific case it's OK.
So I might have XAML that looks like this:
<Button Content="Login"
Command="{Binding LoginCommand}"
CommandParameter="{Binding ElementName=MyPasswordBox}" />
And a LoginCommand
that does something like this:
private void Login(object obj)
{
PasswordBox pwBox = obj as PasswordBox;
SomeBlackBoxClass.ValidatePassword(UserName, pwBox.Password);
}
I suppose you could also run some kind of encryption algorithm on the value and compare the hash of that value to the hash of the user's password too
private void Login(object obj)
{
PasswordBox pwBox = obj as PasswordBox;
var encryptedPassword = SomeLibrary.EncryptValue(pwBox.Password, someKey);
if (encryptedPassword == User.EncryptedPassword)
// Success
}
I'm no expert on the PasswordBox
control or security, but I do know that you don't want to be storing the user's password in plain text anywhere in memory within your application
(Technically, it's stored as plain text in PasswordBox.Password
- you can use something like Snoop to verify this if you want - however typically the PasswordBox doesn't exist for longer than it takes the user to login, and the actual "password" is just text entered by the user, which may or may not be correct. A keylogger could get you the same information.)
回答2:
I have solved this problem by creating a UserControl that exposes a SecureString dependency property that can be bound to. This method keeps the password in a SecureString at all times, and doesn't "break" MVVM.
UserControl
XAML
<UserControl x:Class="Example.PasswordUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<PasswordBox Name="PasswordBox" />
</Grid>
</UserControl>
CS
public partial class PasswordUserControl : UserControl
{
public SecureString Password
{
get { return (SecureString) GetValue(PasswordProperty); }
set { SetValue(PasswordProperty, value); }
}
public static readonly DependencyProperty PasswordProperty =
DependencyProperty.Register("Password", typeof(SecureString), typeof(UserCredentialsInputControl),
new PropertyMetadata(default(SecureString)));
public PasswordUserControl()
{
InitializeComponent();
// Update DependencyProperty whenever the password changes
PasswordBox.PasswordChanged += (sender, args) => {
Password = ((PasswordBox) sender).SecurePassword;
};
}
}
Example usage
Using the control is very straightforward, just bind the password DependencyProperty on the control to a Password property on your ViewModel. The ViewModel's Password property should be a SecureString.
<controls:PasswordUserControl Password="{Binding Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
Change the Mode and UpdateSource trigger on the binding to whatever is best for you.
If you need the password in plain text, the following page describes the proper way to convert between SecureString and string: http://blogs.msdn.com/b/fpintos/archive/2009/06/12/how-to-properly-convert-securestring-to-string.aspx. Of course you shouldn't store the plain text string...
回答3:
depending on your understanding of mvvm (in my way code behind is allowed in some cases)
so i create a PasswordBox and also a named TextBlock
Xaml
<PasswordBox Height="23" Width="156" PasswordChar="*" PasswordChanged="pwBoxUser_PasswordChanged"/>
<TextBlock Height="1" Width="1" Name="MD5pw" Text="{Binding Passwort, UpdateSourceTrigger=PropertyChanged, Mode=OneWayToSource}" VerticalAlignment="Top" />
codebehind
private void pwBoxUser_PasswordChanged(object sender, RoutedEventArgs e)
{
var pBox =sender as PasswordBox;
string blank=pBox.Password;
//to crypt my blank Password
var sMD5 = myMD5.toMD5(blank); //implement your crypt logic here
blank ="";
MD5pw.Text = sMD5;
}
like you can see your password is save and you can easy bind to it
回答4:
Leaving that article aside - there are a few other posts related to this particular question. You can achieve binding using attached properties.
Please see:
- I believe this question is a duplicate of PasswordBox Binding
- The above post points to - http://www.wpftutorial.net/PasswordBox.html