Eu vim através de um problema com a ligação a um P asswordBox
. Parece ser um risco à segurança, mas estou usando o padrão MVVM, por isso desejo contornar isso. Encontrei algum código interessante aqui (alguém usou isso ou algo parecido?)
http://www.wpftutorial.net/PasswordBox.html
Tecnicamente, parece ótimo, mas não tenho certeza de como recuperar a senha.
Basicamente, tenho propriedades no meu LoginViewModel
for Username
e Password
. Username
está bem e está funcionando como é um TextBox
.
Usei o código acima, como indicado, e entrei neste
<PasswordBox ff:PasswordHelper.Attach="True"
ff:PasswordHelper.Password="{Binding Path=Password}" Width="130"/>
Quando eu tinha o PasswordBox
as TextBox
e, em Binding Path=Password
seguida, a propriedade no meu LoginViewModel
foi atualizada.
Meu código é muito simples, basicamente eu tenho um Command
para o meu Button
. Quando pressiono, CanLogin
é chamado e, se retornar verdadeiro, chama Login
.
Você pode ver que eu checo minha propriedade Username
aqui, o que funciona muito bem.
Ao Login
enviar para o meu serviço a Username
e Password
, Username
contém dados do meu View
mas Password
é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 { }
}
É isso que estou fazendo
<TextBox Text="{Binding Path=Username, UpdateSourceTrigger=PropertyChanged}"
MinWidth="180" />
<PasswordBox ff:PasswordHelper.Attach="True"
ff:PasswordHelper.Password="{Binding Path=Password}" Width="130"/>
Eu tenho o meu TextBox
, isso não é problema, mas no meu ViewModel
o Password
está vazio.
Estou fazendo algo errado ou faltando um passo?
Eu coloquei um ponto de interrupção e, com certeza, o código entra na classe auxiliar estática, mas ele nunca atualiza o meu Password
no meu ViewModel
.
Respostas:
Desculpe, mas você está fazendo errado.
As pessoas devem ter as seguintes diretrizes de segurança tatuadas na parte interna das pálpebras:
nunca mantenha senhas de texto sem formatação na memória.
O motivo pelo qual o WPF / Silverlight
PasswordBox
não expõe um DP para aPassword
propriedade está relacionado à segurança.Se o WPF / Silverlight mantivesse um DP
Password
, seria necessário que a estrutura mantivesse a própria senha não criptografada na memória. O que é considerado um vetor de ataque de segurança bastante problemático. oPasswordBox
usa memória criptografada (das sortes) e a única maneira de acessar a senha é através da propriedade CLR.Sugiro que, ao acessar a
PasswordBox.Password
propriedade CLR, você evite colocá-la em qualquer variável ou como valor para qualquer propriedade.Manter sua senha em texto sem formatação na RAM da máquina cliente é um problema de segurança.
Então se livre disso
public string Password { get; set; }
você tem lá em cima.Ao acessar
PasswordBox.Password
, basta tirá-lo e enviá-lo para o servidor o mais rápido possível. Não mantenha o valor da senha por perto e não a trate como faria com qualquer outro texto da máquina cliente. Não mantenha senhas de texto não criptografado na memória.Eu sei que isso quebra o padrão MVVM, mas você nunca deve se ligar a
PasswordBox.Password
Attached DP, armazenar sua senha no ViewModel ou quaisquer outras travessuras semelhantes.Se você está procurando uma solução com arquitetura demais, aqui está uma:
1. Crie a
IHavePassword
interface com um método que retorne o texto não criptografado da senha.2. Tenha seu
UserControl
implementar umaIHavePassword
interface.3. Registre a
UserControl
instância no seu IoC como implementando aIHavePassword
interface.4. Quando uma solicitação de servidor que exige sua senha estiver ocorrendo, ligue para sua IoC para a
IHavePassword
implementação e somente obtenha a senha muito cobiçada.Apenas a minha opinião sobre isso.
- Justin
fonte
Meus 2 centavos:
Desenvolvi uma vez uma caixa de diálogo de login típica (caixas de usuário e senha, mais o botão "Ok") usando WPF e MVVM. Eu resolvi o problema de ligação de senha simplesmente passando o controle PasswordBox como um parâmetro para o comando anexado ao botão "Ok". Então, na visão que eu tinha:
E no ViewModel, o
Execute
método do comando anexado era o seguinte:Isso viola um pouco o padrão MVVM, já que agora o ViewModel sabe algo sobre como o View é implementado, mas nesse projeto em particular eu poderia pagar. Espero que seja útil para alguém também.
fonte
Talvez esteja faltando alguma coisa, mas parece que a maioria dessas soluções complica demais as coisas e acaba com práticas seguras.
Este método não viola o padrão MVVM e mantém a segurança completa. Sim, tecnicamente é um código atrasado, mas nada mais é do que uma ligação de "caso especial". O ViewModel ainda não tem conhecimento da implementação do View, o que, em minha opinião, ocorre se você estiver tentando passar o PasswordBox para o ViewModel.
Code Behind! = Violação automática de MVVM. Tudo depende do que você faz com isso. Nesse caso, estamos apenas codificando manualmente uma ligação, portanto tudo isso é considerado parte da implementação da interface do usuário e, portanto, está ok.
No ViewModel, apenas uma propriedade simples. Fiz "somente gravação", pois não deveria ser necessário recuperá-lo de fora do ViewModel por qualquer motivo, mas não precisa ser. Observe que é um SecureString, não apenas uma string.
No xaml, você configura um manipulador de eventos PasswordChanged.
No código por trás:
Com esse método, sua senha permanece em um SecureString o tempo todo e, portanto, fornece segurança máxima. Se você realmente não se importa com segurança ou precisa da senha de texto não criptografado para um método downstream que a exige (observação: a maioria dos métodos .NET que exigem uma senha também oferece suporte a uma opção SecureString, portanto, talvez você não precise realmente de uma senha em texto não criptografado mesmo que você pense que sim), basta usar a propriedade Senha. Como isso:
(Propriedade ViewModel)
(Código por trás)
Se você quiser manter as coisas fortemente digitadas, poderá substituir a conversão (dinâmica) pela interface do seu ViewModel. Mas, na verdade, as ligações de dados "normais" também não são fortemente digitadas, portanto, não é tão importante assim.
O melhor de todos os mundos - sua senha é segura, seu ViewModel apenas possui uma propriedade como qualquer outra propriedade e seu View é independente, sem necessidade de referências externas.
fonte
Você pode usar este XAML:
E este comando executa o método:
fonte
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=PasswordBox}}"
(note: notRelativeSource Self
).Isso funciona muito bem para mim.
fonte
ICommand
seja implementado no modelo de visualização, esta solução violaria o padrão MVVM.Uma solução simples sem violar o padrão MVVM é introduzir um evento (ou delegar) no ViewModel que colha a senha.
No ViewModel :
public event EventHandler<HarvestPasswordEventArgs> HarvestPassword;
com estes EventArgs:
na visualização , assine o evento ao criar o ViewModel e preencha o valor da senha.
No ViewModel , quando você precisar da senha, poderá disparar o evento e coletar a senha a partir daí:
fonte
WeakEventManager<TEventSource, TEventArgs>
para evitar vazamentos de memória. Muitas vezes, a exibição não terá a mesma duração do modelo de exibição.WeakEventManager<IViewModel, EventArgs>.AddHandler(iViewModelInstance, nameof(IViewModel.Event), eventHandlerMethod);
Passei muito tempo olhando várias soluções. Não gostei da ideia dos decoradores, comportamentos atrapalham a interface de validação, código por trás ... realmente?
O melhor ainda é manter uma propriedade anexada personalizada e vincular à sua
SecureString
propriedade no seu modelo de visualização. Mantenha-o lá o máximo que puder. Sempre que precisar de acesso rápido à senha simples, converta-a temporariamente em uma sequência não segura usando o código abaixo:Certifique-se de permitir que o GC colete seu elemento de interface do usuário; portanto, resista à necessidade de usar um manipulador de eventos estático para o
PasswordChanged
evento noPasswordBox
. Também descobri uma anomalia em que o controle não estava atualizando a interface do usuário ao usar aSecurePassword
propriedade para configurá-la, pelo que estou copiando a senhaPassword
.E o uso de XAML:
Minha propriedade no modelo de exibição era assim:
O
RequiredSecureString
é apenas um validador personalizado simples que possui a seguinte lógica:Aqui você tem. Uma solução MVVM pura completa e testada.
fonte
Eu publiquei um GIST aqui que é uma caixa de senha vinculável.
fonte
ContentControl
você pode simplesmente usar um PasswordBox como o conteúdo e o estilo que, em XAML, conforme você se encaixa. O objetivo doContentControl
é apenas se inscrever noPasswordChanged
evento e expor uma propriedade vinculável bidirecional. Ao todo, são 65 linhas de código e praticamente o que essa classe de decoração faz. Veja aqui a minha lista dos seguintes gist.github.com/leidegre/c7343b8c720000fe3132Esta implementação é um pouco diferente. Você passa uma caixa de senha para o View através da ligação de uma propriedade no ViewModel, ele não usa nenhum parâmetro de comando. O ViewModel permanece ignorante da exibição. Eu tenho um projeto VB vs 2010 que pode ser baixado do SkyDrive. Exemplo de WPF MvvM PassWordBox.zip https://skydrive.live.com/redir.aspx?cid=e95997d33a9f8d73&resid=E95997D33A9F8D73!511
A maneira como estou usando o PasswordBox em um aplicativo Wpf MvvM é bastante simplista e funciona bem para mim. Isso não significa que eu acho que é o caminho correto ou o melhor caminho. É apenas uma implementação do Using PasswordBox e do MvvM Pattern.
Basicamente, você cria uma propriedade pública somente leitura à qual o Modo de Exibição pode se vincular como um PasswordBox (o controle real). Exemplo:
Eu uso um campo de apoio apenas para fazer a auto-inicialização da propriedade.
Em seguida, no Xaml, você vincula o conteúdo de um exemplo de ContentControl ou de contêiner de controle:
A partir daí, você tem controle total da caixa de senhas. Eu também uso um PasswordAccessor (apenas uma função de String) para retornar o valor da senha ao fazer login ou o que mais você desejar. No exemplo, tenho uma propriedade pública em um modelo de objeto de usuário genérico. Exemplo:
No objeto do usuário, a propriedade string da senha é somente leitura, sem nenhum armazenamento de backup, apenas retorna a senha da PasswordBox. Exemplo:
Em seguida, no ViewModel, verifique se o Accessor foi criado e definido como a propriedade PasswordBox.Password '. Exemplo:
Quando eu preciso da palavra-passe Password, digamos, para login, apenas obtenho a propriedade Senha dos Objetos do Usuário que realmente chama a Função para pegar a senha e devolvê-la, então a senha real não é armazenada pelo Objeto do Usuário. Exemplo: estaria no ViewModel
Isso deve resolver. O ViewModel não precisa de nenhum conhecimento dos controles da vista. A View Just vincula à propriedade no ViewModel, não é diferente da View Binding para uma Imagem ou Outro Recurso. Nesse caso, esse recurso (Propriedade) passa a ser um controle de usuário. Ele permite testar como o ViewModel cria e possui a Propriedade e a Propriedade é independente da Vista. Quanto à segurança, não sei o quão boa é essa implementação. Mas, usando uma Função, o Valor não é armazenado na própria Propriedade, apenas acessada pela Propriedade.
fonte
Para resolver o problema do OP sem interromper o MVVM, eu usaria um conversor de valor personalizado e um wrapper para o valor (a senha) que deve ser recuperado da caixa de senha.
No modelo de exibição:
Como o modelo de exibição usa
IWrappedParameter<T>
, ele não precisa ter nenhum conhecimento sobrePasswordBoxWrapper
nemPasswordBoxConverter
. Dessa forma, você pode isolar oPasswordBox
objeto do modelo de visualização e não quebrar o padrão MVVM.Na visualização:
fonte
Embora eu concorde que é importante evitar o armazenamento da senha em qualquer lugar, ainda preciso instanciar o modelo de vista sem uma vista e executar meus testes.
A solução que funcionou para mim foi registrar a função PasswordBox.Password no modelo de exibição e fazer com que o modelo de exibição o invoque ao executar o código de login.
Isso faz significar uma linha de código no codebehind da vista.
Então, no meu Login.xaml eu tenho
e no Login.xaml.cs eu tenho
em LoginViewModel.cs, tenho o PasswordHandler definido
e quando o login precisa acontecer, o código chama o manipulador para obter a senha da visualização ...
Dessa forma, quando eu quiser testar o viewmodel, posso simplesmente definir o PasswordHandler como um método anônimo que permita que eu forneça a senha que desejar usar no teste.
fonte
Imaginei que misturaria minha solução, já que esse é um problema comum ... e ter muitas opções é sempre uma coisa boa.
Eu simplesmente envolvi um
PasswordBox
em umUserControl
e implementei umDependencyProperty
para poder vincular. Eu estou fazendo tudo que posso para evitar armazenar qualquer texto claro na memória, então tudo é feito através de umSecureString
eaPasswordBox.Password
propriedade. Durante oforeach
loop, cada personagem é exposto, mas é muito breve. Honestamente, se você está preocupado com o comprometimento do seu aplicativo WPF com essa breve exposição, você tem problemas de segurança maiores que devem ser tratados.A vantagem disso é que você não está violando nenhuma regra do MVVM, mesmo as "puristas", já que essa é uma regra
UserControl
, portanto é permitido ter código por trás. Ao usá-lo, você pode ter uma comunicação pura entreView
eViewModel
sem que vocêVideModel
esteja ciente de qualquer parteView
ou da fonte da senha. Apenas certifique-se de estar vinculado aoSecureString
seuViewModel
.BindablePasswordBox.xaml
BindablePasswordBox.xaml.cs (Versão 1 - Não há suporte à ligação bidirecional.)
Uso da versão 1:
BindablePasswordBox.xaml.cs (Versão 2 - Possui suporte à ligação bidirecional.)
Uso da versão 2:
fonte
if (Password != secure)
sempre será falso, pois o SecureString não substitui iguais. Alguma ideia?você pode fazê-lo com a propriedade anexada, veja-o .. PasswordBox with MVVM
fonte
Eu usei esse método e passei a caixa de senha, embora isso viole o MVVM, era essencial para mim porque eu estava usando um controle de conteúdo com modelo de dados para o meu login no meu shell, que é um ambiente complexo do shell. Portanto, acessar o código por trás do shell teria sido uma porcaria.
Passando a caixa de senha, eu acho que é o mesmo que acessar o controle do código por trás, tanto quanto eu sei. Eu concordo com senhas, não guardo na memória, etc. Nesta implementação, não tenho propriedade para senha no modelo de exibição.
Comando de botão
ViewModel
fonte
Para mim, essas duas coisas parecem erradas:
PasswordBox
como um parâmetro de comando para o ViewModelTransferir a SecurePassword (instância SecureString), conforme descrito por Steve no CO, parece aceitável. Eu prefiro
Behaviors
codificar por trás e também tinha o requisito adicional de poder redefinir a senha no viewmodel.Xaml (
Password
é a propriedade ViewModel):Comportamento:
fonte
Para iniciantes completos como eu, aqui está uma amostra completa do que foi
Konamiman
sugerido acima. ObrigadoKonamiman
.XAML
ViewModel
fonte
É uma propriedade anexa . Esse tipo de propriedade pode ser aplicado a qualquer tipo de
DependencyObject
, não apenas ao tipo em que é declarado. Portanto, mesmo que seja declarado naPasswordHelper
classe estática, ele é aplicado aoPasswordBox
qual você o usa.Para usar essa propriedade anexada, basta vinculá-la à
Password
propriedade no seu ViewModel:fonte
Eu fiz como:
XAML:
C #:
Funciona para mim!
fonte
Como mencionado anteriormente, a VM não deve ter conhecimento da Visualização, mas passar a PasswordBox inteira parece a abordagem mais simples. Então, talvez, em vez de transmitir o parâmetro passado para o PasswordBox, use o Reflection para extrair a propriedade Password dele. Nesse caso, a VM espera algum tipo de contêiner de senha com a propriedade Password (estou usando RelayCommands do MVMM Light-Toolkit):
Pode ser facilmente testado com classe anônima:
fonte
No aplicativo universal do windows
você pode usar esse código com a propriedade "Senha" e vincular com o modelView
fonte
Para quem está ciente dos riscos que essa implementação impõe, para sincronizar a senha com o seu ViewModel, basta adicionar Mode = OneWayToSource .
XAML
fonte
OneWayToSource
?Aqui está a minha opinião:
O uso de uma propriedade anexada para vincular a senha anula o objetivo de protegê-la. A propriedade Senha de uma caixa de senha não é vinculável por um motivo.
Passar a caixa de senha como parâmetro de comando tornará o ViewModel ciente do controle. Isso não funcionará se você planeja tornar sua plataforma cruzada reutilizável do ViewModel. Não faça sua VM ciente de sua exibição ou de qualquer outro controle.
Não acho que seja necessário introduzir uma nova propriedade, uma interface, assinar eventos alterados por senha ou qualquer outra coisa complicada para uma tarefa simples de fornecer a senha.
XAML
Código atrasado - o uso do código atrasado não viola necessariamente o MVVM. Contanto que você não coloque nenhuma lógica de negócios nela.
ViewModel
fonte
Você encontra uma solução para o PasswordBox no aplicativo de exemplo ViewModel do projeto WPF Application Framework (WAF) .
No entanto, Justin está certo. Não passe a senha como texto sem formatação entre View e ViewModel. Use o SecureString (consulte MSDN PasswordBox).
fonte
Eu usei uma verificação de autenticação seguida por uma sub chamada por uma classe mediadora para o View (que também implementa uma verificação de autenticação) para gravar a senha na classe de dados.
Não é uma solução perfeita; no entanto, corrigiu meu problema de não conseguir mover a senha.
fonte
Estou usando uma solução sucinta e amigável para MVVM que ainda não foi mencionada. Primeiro, nomeio o PasswordBox no XAML:
Em seguida, adiciono uma única chamada de método ao construtor view:
E é isso. O modelo de vista receberá uma notificação quando estiver anexado a uma vista via DataContext e outra notificação quando for desanexado. O conteúdo desta notificação é configurável via lambdas, mas geralmente é apenas uma chamada de setter ou método no modelo de visualização, passando o controle problemático como parâmetro.
Ele pode ser facilmente adaptado ao MVVM com a interface de exibição de exibição em vez de controles filho.
O código acima se baseia na classe auxiliar publicada no meu blog.
fonte
Passei séculos tentando fazer isso funcionar. No final, desisti e usei o PasswordBoxEdit do DevExpress.
É a solução mais simples de sempre, pois permite encadernar sem fazer truques horríveis.
Solução no site DevExpress
Para que conste, não sou afiliado ao DevExpress de forma alguma.
fonte
;) fácil!
fonte
É muito simples . Crie outra propriedade para a senha e vincule-a ao TextBox
Mas todas as operações de entrada são executadas com a propriedade da senha real
private string _Password;
public string Senha {get {return _Password; }
fonte
bem, minha resposta é mais simples, apenas no padrão MVVM
no modo de exibição de classe
a propriedade da senha do PasswordBox que win fornece ou WatermarkPasswordBox que o XCeedtoolkit fornece gera um RoutedEventArgs para que você possa vinculá-lo.
agora na vista xmal
ou
fonte
Envie um
SecureString
para o modelo de visualização usando um comportamento anexado eICommand
Não há nada errado com o code-behind ao implementar o MVVM. MVVM é um padrão arquitetural que visa separar a visualização do modelo / lógica de negócios. O MVVM descreve como atingir esse objetivo de maneira reproduzível (o padrão). Ele não se importa com detalhes da implementação, como estruturar ou implementar a exibição. Ele apenas desenha os limites e define qual é a visão, o modelo de visão e qual é o modelo em termos da terminologia desse padrão.
O MVVM não se importa com o idioma (XAML ou C #) ou compilador (
partial
classes). Ser independente do idioma é uma característica obrigatória de um padrão de design - deve ser neutro no idioma.No entanto, o code-behind tem algumas desvantagens, como tornar a lógica da interface do usuário mais difícil de entender, quando é amplamente distribuída entre XAML e C #. Porém, a implementação mais importante da lógica da interface do usuário ou objetos como modelos, estilos, gatilhos, animações etc. em C # é muito complexa e feia / menos legível do que usar XAML. XAML é uma linguagem de marcação que usa tags e aninhamento para visualizar a hierarquia de objetos. Criar interface do usuário usando XAML é muito conveniente. Embora existam situações em que você pode optar por implementar a lógica da interface do usuário em C # (ou code-behind). Manipular o
PasswordBox
é um exemplo.Por esse motivo, manipular o
PasswordBox
no code-behind manipulando oPasswordBox.PasswordChanged
, não é violação do padrão MVVM.Uma violação clara seria passar um controle (the
PasswordBox
) para o modelo de exibição. Muitas soluções de recomendar este exemplo, baía passando a instância doPasswordBox
quantoICommand.CommandParameter
ao modelo de vista. Obviamente, uma recomendação muito ruim e desnecessária.Se você não se importa em usar C #, mas apenas deseja manter seu arquivo code-behind limpo ou simplesmente deseja encapsular uma lógica de comportamento / interface do usuário, sempre pode usar as propriedades anexadas e implementar um comportamento anexado.
Ao contrário do infame auxiliar de propagação ampla que permite a vinculação à senha de texto sem formatação (risco muito ruim de antipadrão e segurança), esse comportamento usa um
ICommand
para enviar a senha comoSecureString
o modelo de exibição, sempre que issoPasswordBox
gera oPasswordBox.PasswordChanged
evento.MainWindow.xaml
ViewModel.cs
PasswordBox.cs
fonte