Quando eu precisaria de um SecureString no .NET?


179

Estou tentando entender o objetivo do SecureString do .NET. Do MSDN:

Uma instância da classe System.String é imutável e, quando não é mais necessária, não pode ser agendada programaticamente para 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.

Um objeto SecureString é semelhante a um objeto String, pois possui um valor de texto. No entanto, o valor de um objeto SecureString é criptografado automaticamente, pode ser modificado até que o aplicativo o marque como somente leitura e pode ser excluído da memória do computador pelo aplicativo ou pelo coletor de lixo do .NET Framework.

O valor de uma instância do SecureString é criptografado automaticamente quando a instância é inicializada ou quando o valor é modificado. Seu aplicativo pode tornar a instância imutável e impedir modificações adicionais, invocando o método MakeReadOnly.

A criptografia automática é o grande retorno?

E por que não posso apenas dizer:

SecureString password = new SecureString("password");

ao invés de

SecureString pass = new SecureString();
foreach (char c in "password".ToCharArray())
    pass.AppendChar(c);

Que aspecto do SecureString estou ausente?


3
11 anos mais tarde e MS não recomenda SecureStringpara um novo desenvolvimento: github.com/dotnet/platform-compat/blob/master/docs/DE0001.md
Matt Thomas

Respostas:


4

Eu pararia de usar o SecureString. Parece que os caras do PG estão deixando de lado o suporte. Possivelmente até faça isso no futuro - https://github.com/dotnet/apireviews/tree/master/2015-07-14-securestring .

Devemos remover a criptografia do SecureString em todas as plataformas no .NET Core - Devemos obsoleto SecureString - Provavelmente não devemos expor o SecureString no .NET Core


1
O link está inoperante, mas parece continuar: docs.microsoft.com/en-us/dotnet/api/… .. com algumas orientações muito fracas sobre o caminho para a frente, o execpt "não use credenciais" - github.com/dotnet/platform- compat / blob / master / docs / DE0001.md .. você também não ousa usar uma senha para proteger a chave privada dos seus certificados!
Felickz 27/05/19

1
Após 11 anos, esta resposta parece agora ser a 'nova' correta. Os links parecem estar indo obsoleto, mas a orientação de MS é: SecureString não deve ser utilizado
Richard Morgan

109

Algumas partes da estrutura que atualmente usam SecureString:

O principal objetivo é reduzir a superfície de ataque, em vez de eliminá-la. SecureStringssão "fixados" na RAM para que o Garbage Collector não o mova ou faça cópias dele. Também garante que o texto simples não seja gravado no arquivo Swap ou nos dumps principais. A criptografia é mais como ofuscação e, no entanto, não impede um determinado hacker, que seria capaz de encontrar a chave simétrica usada para criptografar e descriptografar.

Como outros já disseram, a razão pela qual você deve criar um SecureStringcaractere por caractere é a primeira falha óbvia de agir de outra maneira: você provavelmente já tem o valor secreto como uma string simples, então qual é o sentido?

SecureStrings são o primeiro passo para resolver um problema de ovos e galinhas, portanto, embora a maioria dos cenários atuais exija convertê-los novamente em seqüências regulares para fazer qualquer uso deles, sua existência na estrutura agora significa um melhor suporte para eles. futuro - pelo menos até um ponto em que seu programa não precise ser o elo mais fraco.


2
Corri através dele olhando para a propriedade Password do ProcessStartInfo; nem prestando atenção ao tipo, apenas o defini como uma sequência regular até o compilador latir para mim.
Richard Morgan

Não será fácil encontrar a chave de criptografia simétrica, já que SecureString é baseado no DPAPI, que does not exatamente armazenar uma chave em texto simples ...
Avid

1
Além disso, não é tanto o problema do ovo e da galinha, já que não substitui a criptografia no armazenamento - mas é uma solução alternativa para seqüências .NET gerenciadas e imutáveis.
Avid

2
"você provavelmente já tem o valor secreto como uma string simples, então qual é o objetivo?" Existe uma resposta para esta pergunta? Parece que, na melhor das hipóteses, é uma solução "melhor que nada" se você deseja manter uma senha armazenada na memória por um período prolongado.
Xr280xr 16/07/2013

2
Que tal ter alguns exemplos simples de código de uso? Acredito que entenderia melhor como e quando usá-lo.
codea

37

Editar : Não use SecureString

A orientação atual agora diz que a classe não deve ser usada. Os detalhes podem ser encontrados neste link: https://github.com/dotnet/platform-compat/blob/master/docs/DE0001.md

Do artigo:

DE0001: SecureString não deve ser usado

Motivação

  • O objetivo SecureStringé evitar ter segredos armazenados na memória do processo como texto sem formatação.
  • No entanto, mesmo no Windows, SecureStringnão existe como um conceito de sistema operacional.
    • Apenas reduz a janela, diminuindo o texto sem formatação; não o impede totalmente, pois o .NET ainda precisa converter a string em uma representação de texto sem formatação.
    • O benefício é que a representação de texto sem formatação não permanece como uma instância de System.String- a vida útil do buffer nativo é mais curta.
  • O conteúdo da matriz não é criptografado, exceto no .NET Framework.
    • No .NET Framework, o conteúdo da matriz de caracteres interna é criptografado. O .NET não oferece suporte à criptografia em todos os ambientes, devido à falta de APIs ou a problemas de gerenciamento de chaves.

Recomendação

Não use SecureStringpara novo código. Ao portar código para o .NET Core, considere que o conteúdo da matriz não está criptografado na memória.

A abordagem geral de lidar com credenciais é evitá-las e confiar em outros meios de autenticação, como certificados ou autenticação do Windows.

Fim da edição: resumo original abaixo

Muitas ótimas respostas; Aqui está uma rápida sinopse do que foi discutido.

A Microsoft implementou a classe SecureString em um esforço para fornecer melhor segurança com informações confidenciais (como cartões de crédito, senhas etc.). Ele fornece automaticamente:

  • criptografia (no caso de despejos de memória ou cache de páginas)
  • fixando na memória
  • capacidade de marcar como somente leitura (para evitar outras modificações)
  • construção segura, NÃO permitindo que uma cadeia constante seja passada

Atualmente, o SecureString é limitado em uso, mas espera uma melhor adoção no futuro.

Com base nessas informações, o construtor do SecureString não deve apenas pegar uma string e dividi-la na matriz char, pois a string soletrada derrota o objetivo do SecureString.

Informação adicional:

  • Uma postagem do blog .NET Security falando sobre o mesmo assunto abordado aqui.
  • E outro um revisitar-lo e mencionar uma ferramenta que pode despejar o conteúdo do SecureString.

Editar: achei difícil escolher a melhor resposta, pois há muitas informações em muitas; Pena que não há opções de resposta assistida.


19

Resposta curta

por que não posso apenas dizer:

SecureString password = new SecureString("password");

Porque agora você tem passwordna memória; sem nenhuma maneira de limpá-lo - que é exatamente o ponto do SecureString .

Resposta longa

O motivo da existência do SecureString é que você não pode usar o ZeroMemory para limpar dados confidenciais quando terminar. Existe para resolver um problema que existe devido ao CLR.

Em um aplicativo nativo comum , você chamaria SecureZeroMemory:

Preenche um bloco de memória com zeros.

Nota : SecureZeroMemory is é idêntico a ZeroMemory, exceto pelo compilador que não será otimizado.

O problema é que você não pode ligar ZeroMemoryou SecureZeroMemorydentro do .NET. E no .NET as strings são imutáveis; você não pode nem substituir o conteúdo da string como em outros idiomas:

//Wipe out the password
for (int i=0; i<password.Length; i++)
   password[i] = \0;

Então o que você pode fazer? Como fornecemos a capacidade do .NET de limpar uma senha ou número de cartão de crédito da memória quando terminamos?

A única maneira de fazer isso seria colocar a string em algum bloco de memória nativo , onde você poderá chamar ZeroMemory. Um objeto de memória nativa, como:

  • um BSTR
  • um HGLOBAL
  • Memória não gerenciada CoTaskMem

SecureString devolve a capacidade perdida

No .NET, Strings não podem ser apagadas quando você terminar com elas:

  • eles são imutáveis; você não pode substituir o conteúdo deles
  • você não pode Disposedeles
  • a limpeza deles está à mercê do coletor de lixo

O SecureString existe como uma maneira de contornar a segurança das strings e garantir sua limpeza quando necessário.

Você fez a pergunta:

por que não posso apenas dizer:

SecureString password = new SecureString("password");

Porque agora você tem passwordna memória; sem nenhuma maneira de limpá-lo. Ele fica preso até que o CLR decida reutilizar essa memória. Você nos colocou de volta onde começamos; um aplicativo em execução com uma senha da qual não podemos nos livrar e onde um despejo de memória (ou Process Monitor) pode ver a senha.

O SecureString usa a API de proteção de dados para armazenar a string criptografada na memória; dessa forma, a string não existirá em arquivos de troca, despejos de memória ou mesmo na janela de variáveis ​​locais com um colega olhando por cima do seu dever.

Como leio a senha?

Então está a pergunta: como interajo com a string? Você absolutamente não quer um método como:

String connectionString = secureConnectionString.ToString()

porque agora você está de volta onde começou - uma senha da qual não pode se livrar. Você deseja forçar os desenvolvedores a manipular a string sensível corretamente - para que ela possa ser apagada da memória.

É por isso que o .NET fornece três funções auxiliares úteis para organizar um SecureString em uma memória não gerenciada:

Você converte a seqüência de caracteres em um blob de memória não gerenciada, manipula-a e limpe-a novamente.

Algumas APIs aceitam SecureStrings . Por exemplo, no ADO.net 4.5, o SqlConnection.Credential usa um conjunto SqlCredential :

SqlCredential cred = new SqlCredential(userid, password); //password is SecureString
SqlConnection conn = new SqlConnection(connectionString);
conn.Credential = cred;
conn.Open();

Você também pode alterar a senha em uma String de conexão:

SqlConnection.ChangePassword(connectionString, cred, newPassword);

E há muitos lugares dentro do .NET em que eles continuam aceitando uma String simples para fins de compatibilidade e, em seguida, mudam rapidamente para um SecureString.

Como colocar texto no SecureString?

Isso ainda deixa o problema:

Como obtenho uma senha no SecureString em primeiro lugar?

Esse é o desafio, mas o objetivo é fazer você pensar em segurança.

Às vezes, a funcionalidade já é fornecida para você. Por exemplo, o controle WPF PasswordBox pode retornar a senha inserida como um SecureString diretamente:

Propriedade PasswordBox.SecurePassword

Obtém a senha atualmente mantida pelo PasswordBox como um SecureString .

Isso é útil porque em todos os lugares que você costumava passar uma string não processada, agora você tem o sistema de tipos queixando-se de que o SecureString é incompatível com a String. Você deseja ir o maior tempo possível antes de ter que converter seu SecureString novamente em uma sequência regular.

Converter um SecureString é fácil:

  • SecureStringToBSTR
  • PtrToStringBSTR

como em:

private static string CreateString(SecureString secureString)
{
    IntPtr intPtr = IntPtr.Zero;
    if (secureString == null || secureString.Length == 0)
    {
        return string.Empty;
    }
    string result;
    try
    {
        intPtr = Marshal.SecureStringToBSTR(secureString);
        result = Marshal.PtrToStringBSTR(intPtr);
    }
    finally
    {
        if (intPtr != IntPtr.Zero)
        {
            Marshal.ZeroFreeBSTR(intPtr);
        }
    }
    return result;
}

Eles realmente não querem que você faça isso.

Mas como faço para obter uma string em um SecureString? Bem, o que você precisa fazer é parar de ter uma senha em uma String em primeiro lugar. Você precisava tê-lo em outra coisa . Mesmo uma Char[]matriz seria útil.

É quando você pode anexar cada caractere e limpar o texto simples quando terminar:

for (int i=0; i < PasswordArray.Length; i++)
{
   password.AppendChar(PasswordArray[i]);
   PasswordArray[i] = (Char)0;
}

Você precisa da sua senha armazenada em alguma memória que possa limpar. Carregue-o no SecureString a partir daí.


tl; dr: SecureString existe para fornecer o equivalente a ZeroMemory .

Algumas pessoas não vêem o sentido de limpar a senha do usuário da memória quando um dispositivo está bloqueado ou de pressionar as teclas pressionadas na memória depois de autenticadas . Essas pessoas não usam o SecureString.


14

Existem muito poucos cenários em que você pode usar o SecureString de forma sensata na versão atual do Framework. É realmente útil apenas para interagir com APIs não gerenciadas - você pode empacotá-lo usando Marshal.SecureStringToGlobalAllocUnicode.

Assim que você o converte em / para um System.String, você perde o objetivo.

A amostra do MSDN gera o SecureString um caractere por vez da entrada do console e passa a string segura para uma API não gerenciada. É bastante complicado e irreal.

Você pode esperar que versões futuras do .NET tenham mais suporte ao SecureString, o que o tornará mais útil, por exemplo:

  • SecureString Console.ReadLineSecure () ou similar para ler a entrada do console em um SecureString sem todo o código complicado da amostra.

  • Substituição WinForms TextBox que armazena sua propriedade TextBox.Text como uma seqüência de caracteres segura para que as senhas possam ser inseridas com segurança.

  • Extensões para APIs relacionadas à segurança para permitir que senhas sejam passadas como SecureString.

Sem o exposto, o SecureString terá um valor limitado.


12

Eu acredito que a razão pela qual você precisa fazer o acréscimo de caracteres em vez de uma instanciação simples é porque, em segundo plano, passar "password" para o construtor do SecureString coloca a string "password" na memória, derrotando o objetivo da string segura.

Ao anexar, você está apenas colocando um personagem de cada vez na memória, o que provavelmente não será adjacente um ao outro, dificultando muito a reconstrução da string original. Eu poderia estar errado aqui, mas foi assim que me foi explicado.

O objetivo da classe é impedir que dados seguros sejam expostos por meio de um despejo de memória ou ferramenta semelhante.


11

A Microsoft descobriu que, em certas instâncias em que o servidor (desktop, qualquer que seja) travasse, havia momentos em que o ambiente de tempo de execução realizava um despejo de memória, expondo o conteúdo do conteúdo da memória. O Secure String o criptografa na memória para impedir que o invasor possa recuperar o conteúdo da string.


5

Um dos grandes benefícios de um SecureString é que ele evita a possibilidade de seus dados serem armazenados em disco devido ao cache da página. Se você tiver uma senha na memória e, em seguida, carregar um programa ou conjunto de dados grande, sua senha poderá ser gravada no arquivo de troca à medida que o programa for paginado com falta de memória. Com um SecureString, pelo menos os dados não ficarão indefinidamente no disco em texto não criptografado.


4

Eu acho que é porque a string é para ser segura, ou seja, um hacker não deve ser capaz de lê-la. Se você inicializá-lo com uma sequência, o hacker poderá ler a sequência original.


4

Bem, como a descrição declara, o valor é armazenado criptografado, o que significa que um despejo de memória do processo não revelará o valor da string (sem algum trabalho bastante sério).

O motivo pelo qual você não pode simplesmente construir um SecureString a partir de uma cadeia constante é porque você teria uma versão não criptografada da cadeia na memória. Limitar a criação da cadeia em partes reduz o risco de manter toda a cadeia na memória de uma só vez.


2
Se eles estão limitando a construção de uma cadeia constante, a linha foreach (char c em "password" .ToCharArray ()) derrotaria isso, não? Deve ser pass.AppendChar ('p'); pass.AppendChar ('a'); etc?
Richard Morgan

Sim, você pode facilmente jogar fora o pouco de proteção que o SecureString oferece. Eles estão tentando dificultar um tiro no próprio pé. Obviamente, deve haver alguma maneira de obter e extrair o valor de um SecureString, ou você não pode usá-lo para nada.
Mark Bessey

1

Outro caso de uso é quando você está trabalhando com aplicativos de pagamento (POS) e simplesmente não pode usar estruturas de dados imutáveis para armazenar dados confidenciais porque é um desenvolvedor cuidadoso. Por exemplo: se eu armazenarei dados confidenciais do cartão ou metadados de autorização em uma sequência imutável, sempre haveria o caso em que esses dados estarão disponíveis na memória por um período significativo de tempo após serem descartados. Não posso simplesmente substituí-lo. Outra grande vantagem é que esses dados confidenciais são mantidos na memória criptografada.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.