292 votes

Comment lier à un PasswordBox en MVVM

J'ai rencontré un problème avec la liaison à un PasswordBox. Il semble qu'il est un risque pour la sécurité, mais je suis en utilisant le pattern MVVM je souhaite donc passer outre. J'ai trouvé intéressant de code ici (quelqu'un a utilisé ce ou quelque chose de similaire?)

http://www.wpftutorial.net/PasswordBox.html

Techniquement ressemble beaucoup, mais je suis pas sûr de savoir comment récupérer le mot de passe.

J'ai pratiquement propriétés dans mon LoginViewModel pour Username et Password. Username est fine et travaille comme c'est un TextBox.

J'ai utilisé le code ci-dessus comme indiqué et est entré dans ce

<PasswordBox ff:PasswordHelper.Attach="True"
    ff:PasswordHelper.Password="{Binding Path=Password}" Width="130"/>

Quand j'ai eu l' PasswordBox comme TextBox et Binding Path=Password alors la propriété dans mes LoginViewModel a été mis à jour.

Mon code est très simple, en gros j'ai un Command mon Button. Quand je le presse CanLogin est appelée et si elle retourne true il appelle Login.
Vous pouvez le voir j'ai vérifier ma propriété pour Username ici qui fonctionne très bien.

En Login - je envoyer à mon service un Username et Password, Username contient des données à partir de mon View mais Password est Null|Empty

private DelegateCommand loginCommand;

    public string Username { get; set; }
    public string Password { get; set; }


    public ICommand LoginCommand
    {
        get
        {
            if (loginCommand == null)
            {
                loginCommand = new DelegateCommand(
                    Login, CanLogin );
            }
            return loginCommand;
        }
    }

    private bool CanLogin()
    {
        return !string.IsNullOrEmpty(Username);
    }

    private void Login()
    {
        bool result = securityService.IsValidLogin(Username, Password);

        if (result) { }
        else { }
    }

C'est ce que je fais

<TextBox Text="{Binding Path=Username, UpdateSourceTrigger=PropertyChanged}"
         MinWidth="180" />

<PasswordBox ff:PasswordHelper.Attach="True" 
             ff:PasswordHelper.Password="{Binding Path=Password}" Width="130"/>

J'ai mon TextBox, ce n'est pas un problème, mais dans mon ViewModel le Password est vide.

Suis-je en train de faire quelque chose de mauvais ou raté une étape?

J'ai mis un point d'arrêt et bien sûr le code de l'entrée de la statique de la classe helper mais c'est jamais les mises à jour de mon Password mon ViewModel.

238voto

Steve In CO Points 21

Peut-être que je manque quelque chose, mais il semble que la plupart de ces solutions de compliquer les choses et de sécuriser les pratiques.

Cette méthode ne viole pas le pattern MVVM et maintient une sécurité complète. Oui, techniquement, il est un code derrière, mais il n'est rien de plus qu'un "cas particulier" de la liaison. Ce Dernier n'a toujours pas connaissance de l'implémentation de la Vue, qui dans mon esprit qu'il ne si vous essayez de passer la PasswordBox dans le ViewModel.

Code Derrière != Automatique MVVM violation. Tout dépend de ce que vous faites avec lui. Dans ce cas, nous sommes juste manuellement l'encodage d'une liaison, de sorte que son tout considéré comme faisant partie de l'INTERFACE utilisateur de mise en œuvre et, par conséquent, est ok.

Dans le ViewModel, juste une simple propriété. Je l'ai fait "écriture seule" car il ne devrait pas être nécessaire de le récupérer à partir de l'extérieur du ViewModel pour une raison quelconque, mais il n'a pas à être. Notez qu'il est un SecureString, et pas seulement une chaîne de caractères.

public SecureString SecurePassword { private get; set; }

Dans le code xaml, vous avez défini un PasswordChanged gestionnaire d'événement.

<PasswordBox PasswordChanged="PasswordBox_PasswordChanged"/>

Dans le code behind:

private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
{
    if (this.DataContext != null)
    { ((dynamic)this.DataContext).SecurePassword = ((PasswordBox)sender).SecurePassword; }
}

Avec cette méthode, votre mot de passe reste dans un SecureString à tout moment et, par conséquent, offre un maximum de sécurité. Si vous avez vraiment ne se soucient pas de la sécurité ou vous avez besoin du mot de passe en clair pour un en aval de la méthode que l'exige (remarque: la plupart des .NET méthodes qui nécessitent un mot de passe également en charge un SecureString option, de sorte que vous ne pouvez pas vraiment besoin d'un mot de passe en clair, même si vous pensez que vous faites), vous pouvez utiliser le Mot de passe de propriété à la place. Comme ceci:

(ViewModel de la propriété)

public string Password { private get; set; }

(Code derrière)

private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
{
    if (this.DataContext != null)
    { ((dynamic)this.DataContext).Password = ((PasswordBox)sender).Password; }
}

Si vous voulez garder les choses fortement typé, il est possible de substituer (dynamique) en fonte avec de l'interface de votre ViewModel. Mais vraiment, "normal" liaisons de données ne sont pas fortement typé, donc ce n'est pas que les grandes d'un accord.

private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
{
    if (this.DataContext != null)
    { ((IMyViewModel)this.DataContext).Password = ((PasswordBox)sender).Password; }
}

Donc, le meilleur de tous les mondes - votre mot de passe est sécurisé, votre ViewModel vient d'une propriété comme tout autre bien, et votre point de Vue est autonome avec pas de références externes requis.

219voto

Konamiman Points 20578

Mes 2 cents:

J'ai développé une fois un typique de la boîte de dialogue de connexion (utilisateur et mot de passe, plus le bouton "Ok") à l'aide de WPF et de MVVM. J'ai résolu le mot de passe de liaison question par un simple passage de la PasswordBox le contrôle de lui-même en tant que paramètre à la commande attachés à la touche "Ok". Donc dans l'idée que j'avais:

<PasswordBox Name="txtPassword" VerticalAlignment="Top" Width="120" />
<Button Content="Ok" Command="{Binding Path=OkCommand}"
   CommandParameter="{Binding ElementName=txtPassword}"/>

Et dans le ViewModel, l' Execute méthode de la commande a été comme suit:

void Execute(object parameter)
{
    var passwordBox = parameter as PasswordBox;
    var password = passwordBox.Password;
    //Now go ahead and check the user name and password
}

Cette légère viole le modèle MVVM depuis maintenant le ViewModel sait quelque chose sur la façon dont la Vue est mis en œuvre, mais dans ce projet particulier, je pouvais me le permettre. J'espère que c'est utile pour quelqu'un d'aussi bien.

185voto

JustinAngel Points 13219

Désolé, mais vous le faites mal.

Les gens devraient avoir la suite de sécurité de la directive tatoué sur l'intérieur de ses paupières:
Ne gardez jamais de texte en clair les mots de passe en mémoire.

La raison pour laquelle le WPF/Silverlight PasswordBox n'expose pas de DP pour le Mot de passe de propriété est lié à la sécurité.
Si WPF/Silverlight ont été de garder une DP pour le Mot de passe il faudrait le cadre de garder le mot de passe non crypté dans la mémoire. Qui est considéré comme une source de sécurité vecteur d'attaque. Le PasswordBox utilise cryptés la mémoire (de toutes sortes) et la seule façon d'accéder au mot de passe est par le CLR de la propriété.

Je dirais que, lors de l'accès à la PasswordBox.Mot de passe CLR bien vous feriez s'abstenir de le placer dans n'importe quelle variable ou une valeur pour une propriété.
Garder votre mot de passe en texte clair sur l'ordinateur client RAM de sécurité est un non-non.
Afin de se débarrasser de cette "public string Mot de passe { get; set; }" vous avez là-haut.

Lors de l'accès à PasswordBox.Mot de passe, juste les faire et de les expédier vers le serveur le plus vite possible. Ne gardez pas la valeur du mot de passe autour et ne pas le traiter comme tout autre client de la machine de texte. Ne gardez pas de texte en clair les mots de passe en mémoire.

Je sais que cela rompt avec le modèle MVVM, mais vous ne devriez jamais se lier à PasswordBox.Le mot de passe Attaché DP, de stocker votre mot de passe dans le ViewModel ou tout autre manigances.

Si vous êtes à la recherche d'une plus-l'architecture de la solution, en voici une:
1. Créer le IHavePassword interface avec une méthode qui renvoie le mot de passe en texte clair.
2. Votre UserControl mettre en œuvre un IHavePassword interface.
3. Enregistrer l'instance UserControl avec votre Cio, ainsi que la mise en œuvre de la IHavePassword interface.
4. Lorsqu'un serveur de demande de la demande de votre mot de passe, appelez votre Cio pour la IHavePassword mise en œuvre et d'obtenir le très convoité mot de passe.

Juste mon prendre sur elle.

-- Justin

14voto

Cela fonctionne très bien pour moi.

 <Button Command="{Binding Connect}" 
CommandParameter="{Binding ElementName=MyPasswordBox}"/>
 

10voto

Jan Willem B Points 2702

Une solution simple, sans violer le pattern MVVM est de présenter un événement (ou son délégué) dans le ViewModel qui récolte le mot de passe.

Dans le ViewModel:

public event EventHandler<HarvestPasswordEventArgs> HarvestPassword;

avec ces EventArgs:

class HarvestPasswordEventArgs : EventArgs
{
    public string Password;
}

dans la Vue, vous inscrire à l'événement sur la création de ce Dernier et de remplir le mot de passe de la valeur.

_viewModel.HarvestPassword += (sender, args) => 
    args.Password = passwordBox1.Password;

Dans le ViewModel, lorsque vous avez besoin du mot de passe, vous pouvez déclencher l'événement et de la récolte, le mot de passe à partir de là:

if (HarvestPassword == null)
  //bah 
  return;

var pwargs = new HarvestPasswordEventArgs();
HarvestPassword(this, pwargs);

LoginHelpers.Login(Username, pwargs.Password);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X