Fique à vontade para me corrigir se minhas suposições estiverem erradas aqui, mas deixe-me explicar por que estou perguntando.
Retirado do MSDN, a SecureString
:
Representa o texto que deve ser mantido em sigilo. O texto é criptografado para privacidade ao ser usado e excluído da memória do computador quando não é mais necessário.
Eu entendo isso, faz todo o sentido armazenar uma senha ou outras informações particulares em um SecureString
over a System.String
, porque você pode controlar como e quando ele é realmente armazenado na memória, porque System.String
:
é imutável e, quando não é mais necessário, não pode ser programado de forma programática para a coleta de lixo; isto é, a instância é somente leitura depois de criada e não é possível prever quando a instância será excluída da memória do computador. Conseqüentemente, se um objeto String contiver informações confidenciais, como senha, número do cartão de crédito ou dados pessoais, existe o risco de as informações serem reveladas depois de usadas, porque o aplicativo não pode excluir os dados da memória do computador.
No entanto, no caso de um aplicativo GUI (por exemplo, um cliente ssh), SecureString
ele deve ser construído a partir de a System.String
. Todos os controles de texto usam uma sequência como seu tipo de dados subjacente .
Portanto, isso significa que toda vez que o usuário pressiona uma tecla, a string antiga que estava lá é descartada e uma nova string é criada para representar qual é o valor dentro da caixa de texto, mesmo se estiver usando uma máscara de senha. E não podemos controlar quando ou se algum desses valores é descartado da memória .
Agora é hora de fazer login no servidor. Adivinha? Você precisa passar uma string pela conexão para autenticação . Então, vamos converter nosso SecureString
em um System.String
.... e agora temos uma string no heap sem nenhuma maneira de forçá-lo a passar pela coleta de lixo (ou escrever 0 no seu buffer).
Meu ponto é : não importa o que você faz, em algum lugar ao longo da linha, que SecureString
está indo para ser convertido em um System.String
, o que significa que vai pelo menos existem na pilha em algum ponto (sem qualquer garantia de coleta de lixo).
O que eu quero dizer não é : se existem maneiras de contornar o envio de uma string para uma conexão ssh ou contornar o fato de um controle armazenar uma string (faça um controle personalizado). Para esta pergunta, você pode substituir "conexão ssh" por "formulário de login", "formulário de registro", "formulário de pagamento", "alimentos que você alimentaria seu filhote de cachorro, mas não seus filhos", etc.
- Então, em que momento o uso de um
SecureString
realmente se torna prático? - Vale a pena o tempo extra de desenvolvimento para erradicar completamente o uso de um
System.String
objeto? - O
SecureString
objetivo é simplesmente reduzir a quantidade de tempoSystem.String
em que está na pilha (reduzindo o risco de mudar para um arquivo de troca físico)? - Se um invasor já tem os meios para uma inspeção de pilha, então provavelmente (A) já tem meios para ler as teclas digitadas, ou (B) já possui fisicamente a máquina ... Então, usando um
SecureString
impedimento ele chegaria ao dados de qualquer maneira? - Isso é apenas "segurança através da obscuridade"?
Desculpe se eu estou colocando as perguntas muito grossas, a curiosidade acabou de me superar. Sinta-se à vontade para responder a uma ou todas as minhas perguntas (ou diga que minhas suposições estão completamente erradas). :)
SecureString
não é realmente uma string segura. É apenas uma maneira de diminuir a janela de tempo em que alguém pode inspecionar sua memória e obter com êxito os dados confidenciais. Isso não é à prova de balas e não era para ser. Mas os pontos que você está levantando são muito válidos. Relacionados: stackoverflow.com/questions/14449579/...Respostas:
Na verdade, existem usos muito práticos do
SecureString
.Você sabe quantas vezes eu já vi esses cenários? (a resposta é: muitos!):
RedGate
software que poderia capturar o "valor" de variáveis locais em caso de exceções, incrivelmente útil. No entanto, posso imaginar que ele registrará "senhas de string" acidentalmente.Você sabe como evitar todos esses problemas?
SecureString
. Geralmente, garante que você não cometa erros tolos como tal. Como isso evita? Garantindo que a senha seja criptografada na memória não gerenciada e que o valor real só possa ser acessado quando você tiver 90% de certeza do que está fazendo.No sentido,
SecureString
funciona com bastante facilidade:1) Tudo está criptografado
2) Chamadas de usuário
AppendChar
3) Descriptografe tudo em MEMÓRIA NÃO GERENCIADA e adicione o personagem
4) Criptografe tudo novamente em MEMÓRIA NÃO GERENCIADA.
E se o usuário tiver acesso ao seu computador? Um vírus seria capaz de obter acesso a tudo isso
SecureStrings
? Sim. Tudo o que você precisa fazer é conectar-seRtlEncryptMemory
quando a memória estiver sendo descriptografada, você obterá a localização do endereço de memória não criptografado e a lerá. Voila! De fato, você pode criar um vírus que constantemente procura o usoSecureString
e registra todas as atividades nele. Não estou dizendo que será uma tarefa fácil, mas pode ser feita. Como você pode ver, a "força" deSecureString
desaparece completamente quando há um usuário / vírus em seu sistema.Você tem alguns pontos em sua postagem. Claro, se você usar alguns dos controles da interface do usuário que contêm internamente uma "senha de string", usar o real
SecureString
não é tão útil. Embora, ainda assim, possa proteger contra alguma estupidez listada acima.Além disso, como outros observaram, o WPF suporta o PasswordBox, que é usado
SecureString
internamente por meio da propriedade SecurePassword .A linha inferior é; se você tiver dados confidenciais (senhas, cartões de crédito,
SecureString
etc. ), use . É isso que o C # Framework está seguindo. Por exemplo, aNetworkCredential
classe armazena a senha comoSecureString
. Se você olhar para isso , poderá ver mais de 80 usos diferentes no .NET framework deSecureString
.Há muitos casos em que você precisa converter
SecureString
para string, porque algumas API esperam isso.O problema usual é:
Você levantou um ponto positivo: o que acontece quando
SecureString
é convertidostring
? Isso só pode acontecer por causa do primeiro ponto. Por exemplo, a API não sabe que são dados confidenciais. Eu pessoalmente não vi isso acontecer. Obter string do SecureString não é assim tão simples.Não é simples por uma razão simples ; ele nunca teve a intenção de permitir que o usuário convertesse o SecureString em string, como você afirmou: o GC entrará em ação. Se você se vê fazendo isso, precisa se afastar e se perguntar: por que estou fazendo isso ou preciso mesmo? isso porque?
Há um caso interessante que eu vi. Ou seja, a função WinApi LogonUser usa LPTSTR como uma senha, o que significa que você precisa ligar
SecureStringToGlobalAllocUnicode
. Isso basicamente fornece uma senha não criptografada que fica na memória não gerenciada. Você precisa se livrar disso assim que terminar:Você sempre pode estender a
SecureString
classe com um método de extensão, comoToEncryptedString(__SERVER__PUBLIC_KEY)
, que fornece umastring
instânciaSecureString
criptografada usando a chave pública do servidor. Somente o servidor pode descriptografá-lo. Problema resolvido: a Coleta de Lixo nunca verá a sequência "original", pois você nunca a expõe na memória gerenciada. É exatamente isso que está sendo feito emPSRemotingCryptoHelper
(EncryptSecureStringCore(SecureString secureString)
).E como algo muito relacionado: o Mono SecureString não criptografa . A implementação foi comentada porque .. aguarde .. "De alguma forma, causa quebra de teste da unidade" , o que traz ao meu último ponto:
SecureString
não é suportado em qualquer lugar. Se a plataforma / arquitetura não suportarSecureString
, você receberá uma exceção. Há uma lista de plataformas suportadas na documentação.fonte
SecureString
seria muito mais prático se houvesse um mecanismo para acessar caracteres individuais dos mesmos. A eficiência desse mecanismo pode ser aprimorada com um tipo de estruturaSecureStringTempBuffer
sem nenhum membro público, que pode ser passadoref
para oSecureString
método "ler um caractere" [se a criptografia / descriptografia for processada em blocos de 8 caracteres, essa estrutura poderá conter dados para 8 caracteres e a localização do primeiro desses caracteres dentro deSecureString
].SecureString
lo, precisa ser usado por toda a pilha.EngineSettings
no seu método de extensão?SecureString
não deve ser usada para novos desenvolvimentos ?Existem poucas questões em suas suposições.
Primeiro de tudo, a classe SecureString não possui um construtor String. Para criar um, você aloca um objeto e depois acrescenta os caracteres.
No caso de uma GUI ou console, você pode facilmente passar cada tecla pressionada para uma string segura.
A classe é projetada de uma maneira que você não pode, por engano, acessar o valor que está armazenado. Isso significa que você não pode obter a
string
senha diretamente dela.Portanto, para usá-lo, por exemplo, para autenticar através da Web, você precisará usar classes apropriadas que também são seguras.
Na estrutura .NET, você tem algumas classes que podem usar o SecureString
(Mais)
Para concluir, a classe SecureString pode ser útil, mas requer mais atenção do desenvolvedor.
Tudo isso, com exemplos, está bem descrito na documentação do SecureString do MSDN
fonte
SecureString
pela rede sem serializá-lo em umstring
? Eu acho que o ponto da pergunta da OP é que chega um momento em que você deseja realmente usar o valor mantido com segurança em aSecureString
, o que significa que você precisa tirá-lo de lá e não é mais seguro. Em toda a biblioteca de classes do Framework, praticamente não existem métodos que aceitem aSecureString
entrada como diretamente (tanto quanto eu posso ver), então qual é o sentido de manter um valor dentro de umaSecureString
em primeiro lugar?char[]
e enviando cadachar
um em um soquete - não criando objetos colecionáveis no processo.Um SecureString é útil se:
Você o constrói caractere por caractere (por exemplo, da entrada do console) ou obtém-o de uma API não gerenciada
Você o usa passando-o para uma API não gerenciada (SecureStringToBSTR).
Se você alguma vez convertê-lo em uma sequência gerenciada, será derrotado.
UPDATE em resposta ao comentário
Após a conversão para um BSTR, o componente não gerenciado que consome o BSTR pode zerar a memória. A memória não gerenciada é mais segura no sentido de que pode ser redefinida dessa maneira.
No entanto, existem muito poucas APIs no .NET Framework que oferecem suporte ao SecureString, portanto, você está certo ao dizer que é de valor muito limitado hoje.
O principal caso de uso que eu veria é em um aplicativo cliente que requer que o usuário insira um código ou senha altamente confidencial. A entrada do usuário pode ser usada caractere por caractere para criar um SecureString; depois, isso pode ser passado para uma API não gerenciada, que zera o BSTR recebido após o uso. Qualquer despejo de memória subsequente não conterá a seqüência sensível.
Em um aplicativo para servidor, é difícil ver onde isso seria útil.
ATUALIZAÇÃO 2
Um exemplo de uma API .NET que aceita um SecureString é esse construtor para a classe X509Certificate . Se você digitar com ILSpy ou similar, verá que o SecureString é convertido internamente em um buffer não gerenciado (
Marshal.SecureStringToGlobalAllocUnicode
), que é zerado ao terminar com (Marshal.ZeroFreeGlobalAllocUnicode
).fonte
SecureString
como entrada, qual seria o uso deles? Você sempre precisaria converter novamente parastring
mais cedo ou mais tarde (ou,BSTR
como você mencionou, o que não parece mais seguro). Então, por que se preocuparSecureString
?A Microsoft não recomenda o uso
SecureString
de códigos mais recentes.Na documentação da classe SecureString :
O que recomenda:
fonte
Como você identificou corretamente,
SecureString
oferece uma vantagem específica sobrestring
: apagamento determinístico. Há dois problemas com esse fato:SecureString
. Isso significa que você deve ter cuidado para nunca criar um imutável gerenciado pelo GCstring
ou qualquer outro buffer que armazene as informações confidenciais (ou será necessário acompanhar isso também). Na prática, isso nem sempre é fácil de conseguir, porque muitas APIs oferecem apenas uma maneira de trabalharstring
, nãoSecureString
. E mesmo se você conseguir tudo certo ...SecureString
protege contra tipos muito específicos de ataque (e para alguns deles, nem é tão confiável). Por exemplo,SecureString
permite reduzir a janela de tempo em que um invasor pode despejar a memória do seu processo e extrair com êxito as informações confidenciais (novamente, como você apontou corretamente), mas esperando que a janela seja muito pequena para o invasor tirar um instantâneo da sua memória não é considerado segurança.Então, quando você deve usá-lo? Somente quando você estiver trabalhando com algo que possa permitir que você trabalhe
SecureString
para todas as suas necessidades e, mesmo assim, você deve estar ciente de que isso é seguro apenas em circunstâncias específicas.fonte
O texto abaixo é copiado do analisador de código estático HP Fortify
Resumo: O método PassString () no PassGenerator.cs armazena dados confidenciais de maneira insegura (por exemplo, em string), possibilitando extrair os dados inspecionando a pilha.
Explicação: Dados confidenciais (como senhas, números de previdência social, números de cartão de crédito etc.) armazenados na memória podem vazar se forem armazenados em um objeto String gerenciado. Os objetos de sequência não são fixados; portanto, o coletor de lixo pode realocar esses objetos à vontade e deixar várias cópias na memória. Como esses objetos não são criptografados por padrão, qualquer pessoa que possa ler a memória do processo poderá ver o conteúdo. Além disso, se a memória do processo for trocada para o disco, o conteúdo não criptografado da sequência será gravado em um arquivo de troca. Por fim, como os objetos String são imutáveis, a remoção do valor de uma String da memória pode ser feita apenas pelo coletor de lixo CLR. O coletor de lixo não precisa ser executado, a menos que o CLR esteja com pouca memória, portanto não há garantia de quando a coleta de lixo ocorrerá.
Recomendações: em vez de armazenar dados confidenciais em objetos como Strings, armazene-os em um objeto SecureString. Cada objeto armazena seu conteúdo em um formato criptografado na memória o tempo todo.
fonte
Eu gostaria de abordar este ponto:
Um invasor pode não ter acesso total ao computador e ao aplicativo, mas pode ter meios para acessar algumas partes da memória do processo. Geralmente, é causada por erros, como saturação de buffer, quando entradas especialmente construídas podem fazer com que o aplicativo exponha ou substitua parte da memória.
Coração vazando memória
Tome Heartbleed, por exemplo. Solicitações especialmente construídas podem fazer com que o código exponha partes aleatórias da memória do processo ao invasor. Um invasor pode extrair certificados SSL da memória, mas a única coisa de que precisa é apenas usar uma solicitação malformada.
No mundo do código gerenciado, as saturações de buffer se tornam um problema com muito menos frequência. E no caso do WinForms, os dados já estão armazenados de maneira insegura e você não pode fazer nada sobre isso. Isso torna a proteção
SecureString
praticamente inútil.No entanto, a GUI pode ser programada para uso
SecureString
e, nesse caso, a redução da janela de disponibilidade de senha na memória pode valer a pena. Por exemplo, PasswordBox.SecurePassword do WPF é do tipoSecureString
.fonte
Há algum tempo, tive que criar uma interface AC # contra um gateway de pagamento com cartão de crédito java e era necessário criptografar chaves de comunicações seguras compatíveis. Como a implementação do Java era bastante específica e eu tive que trabalhar com os dados protegidos de uma determinada maneira.
Achei esse design bastante fácil de usar e mais fácil do que trabalhar com o SecureString… para quem gosta de usar… fique à vontade, sem restrições legais :-). Observe que essas classes são internas, pode ser necessário torná-las públicas.
fonte