Por que o char é preferido em relação ao String para senhas?

No Swing, o campo de senha tem um getPassword() (retorna char[] ) em vez do método usual getText() (retorna String ). Da mesma forma, me deparei com uma sugestão para não usar o String para manipular senhas.

Por que a String representa uma ameaça à segurança quando se trata de senhas? Parece inconveniente usar char[] .

Cordas são imutáveis . Isso significa que, uma vez que você tenha criado a String , se outro processo puder despejar memory, não há como (além de refletir ) você pode se livrar dos dados antes que a garbage collection seja iniciada.

Com um array, você pode apagar explicitamente os dados depois de terminar. Você pode sobrescrever a matriz com qualquer coisa que desejar e a senha não estará presente em nenhum lugar do sistema, mesmo antes da garbage collection.

Então, sim, isso é uma preocupação de segurança – mas até mesmo usar o char[] reduz apenas a janela de oportunidade para um invasor, e é somente para esse tipo específico de ataque.

Conforme observado nos comentários, é possível que matrizes sendo movidas pelo coletor de lixo deixem cópias dispersas dos dados na memory. Eu acredito que isso é específico da implementação – o coletor de lixo pode limpar toda a memory, evitando esse tipo de coisa. Mesmo que isso aconteça, ainda há o tempo durante o qual o char[] contém os caracteres reais como uma janela de ataque.

Embora outras sugestões aqui pareçam válidas, existe outro bom motivo. Com o Simple String você tem chances muito maiores de imprimir acidentalmente a senha em logs , monitores ou algum outro local inseguro. char[] é menos vulnerável.

Considere isto:

 public static void main(String[] args) { Object pw = "Password"; System.out.println("String: " + pw); pw = "Password".toCharArray(); System.out.println("Array: " + pw); } 

Impressões:

 String: Password Array: [C@5829428e 

Para citar um documento oficial, o guia Java Cryptography Architecture diz isso sobre as senhas char[] vs. String (sobre criptografia baseada em senha, mas isso é mais geralmente sobre senhas, é claro):

Parece lógico coletar e armazenar a senha em um object do tipo java.lang.String . No entanto, aqui está a ressalva: Os Object do tipo String são imutáveis, ou seja, não há methods definidos que permitam alterar (sobrescrever) ou zerar o conteúdo de uma String após o uso. Esse recurso torna os objects String impróprios para armazenar informações confidenciais de segurança, como senhas de usuários. Você deve sempre coletar e armazenar informações confidenciais de segurança em uma matriz de char .

A Diretriz 2-2 das Diretrizes de Codificação Segura para a Linguagem de Programação Java, Versão 4.0, também diz algo semelhante (embora seja originalmente no contexto do logging):

Diretriz 2-2: Não registre informações altamente confidenciais

Algumas informações, como números de segurança social (SSNs) e senhas, são altamente confidenciais. Esta informação não deve ser mantida por mais tempo do que o necessário nem onde possa ser vista, mesmo pelos administradores. Por exemplo, ele não deve ser enviado para arquivos de log e sua presença não deve ser detectada por meio de pesquisas. Alguns dados transitórios podem ser mantidos em estruturas de dados mutáveis, como matrizes de caracteres, e limpos imediatamente após o uso. A limpeza de estruturas de dados reduziu a eficácia em sistemas de tempo de execução Java típicos à medida que os objects são movidos na memory de forma transparente para o programador.

Essa diretriz também tem implicações para a implementação e uso de bibliotecas de nível inferior que não possuem conhecimento semântico dos dados com os quais estão lidando. Por exemplo, uma biblioteca de análise de cadeia de caracteres de baixo nível pode registrar o texto em que está trabalhando. Um aplicativo pode analisar um SSN com a biblioteca. Isso cria uma situação em que os SSNs estão disponíveis para administradores com access aos arquivos de log.

Matrizes de char[] ( char[] ) podem ser apagadas após o uso, definindo cada caractere como zero e não como Strings. Se alguém puder ver a imagem da memory de alguma forma, ela poderá ver uma senha em texto sem formatação se as Strings forem usadas, mas se char[] for usado, depois de limpar os dados com 0, a senha será segura.

Algumas pessoas acreditam que você precisa sobrescrever a memory usada para armazenar a senha quando não precisar mais dela. Isso reduz a janela de tempo que um invasor deve ler a senha do seu sistema e ignora completamente o fato de que o invasor já precisa de access suficiente para seqüestrar a memory da JVM para fazer isso. Um invasor com tanto access pode capturar seus events principais, tornando isso completamente inútil (AFAIK, então, por favor, corrija-me se eu estiver errado).

Atualizar

Graças aos comentários, tenho que atualizar minha resposta. Aparentemente, há dois casos em que isso pode adicionar uma melhoria de segurança (muito) menor, uma vez que reduz o tempo que uma senha pode ser colocada no disco rígido. Ainda acho que é um exagero para a maioria dos casos de uso.

  • Seu sistema de destino pode estar mal configurado ou você deve presumir que é e precisa ser paranóico sobre os dumps principais (pode ser válido se os sistemas não forem gerenciados por um administrador).
  • Seu software tem que ser excessivamente paranóico para evitar vazamentos de dados com o invasor obtendo access ao hardware – usando coisas como TrueCrypt (descontinuado), VeraCrypt ou CipherShed .

Se possível, desabilitar os core dumps e o arquivo de troca cuidaria dos dois problemas. No entanto, eles exigiriam direitos de administrador e poderiam reduzir a funcionalidade (menos memory para uso) e puxar RAM de um sistema em execução ainda seria uma preocupação válida.

  1. As strings são imutáveis em Java se você armazenar a senha como texto simples, ela ficará disponível na memory até que Garbage collector a apague e, como Strings são usados ​​no pool de String para reutilização, há uma grande chance de permanecer na memory por um longo tempo. duração, o que representa uma ameaça à segurança. Desde que qualquer pessoa que tenha access ao despejo de memory pode encontrar a senha em texto não criptografado
  2. Recomendação Java usando o método getPassword() do JPasswordField que retorna um método char [] e obsoleto getText() que retorna a senha em texto claro indicando o motivo da segurança.
  3. toString () sempre existe o risco de imprimir texto simples no arquivo de log ou no console, mas se usar o Array, você não imprimirá o conteúdo do array, em vez disso, o local da memory será impresso.

     String strPwd = "passwd"; char[] charPwd = new char[]{'p','a','s','s','w','d'}; System.out.println("String password: " + strPwd ); System.out.println("Character password: " + charPwd ); 

    Senha da string: passwd

    Senha do personagem: [C @ 110b2345

Considerações finais: Embora usar char [] não seja suficiente, você precisa apagar o conteúdo para ser mais seguro. Eu também sugiro trabalhar com senha criptografada ou hash em vez de texto sem formatação e limpá-lo da memory assim que a autenticação for concluída.

Não acho que seja uma sugestão válida, mas posso pelo menos adivinhar o motivo.

Eu acho que a motivação é querer ter certeza de que você pode apagar todos os vestígios da senha na memory imediatamente e com certeza depois de ser usada. Com um char[] você poderia sobrescrever cada elemento da matriz com um espaço em branco ou algo com certeza. Você não pode editar o valor interno de uma String dessa maneira.

Mas isso sozinho não é uma boa resposta; por que não apenas garantir que uma referência ao char[] ou String não escape? Então não há problema de segurança. Mas a coisa é que os objects String podem ser intern() em teoria) e mantidos vivos dentro do pool constante. Eu suponho que usar char[] proíba essa possibilidade.

A resposta já foi dada, mas gostaria de compartilhar um problema que descobri recentemente com bibliotecas padrão Java. Enquanto eles tomam muito cuidado agora de replace strings de senha por char[] todos os lugares (o que obviamente é uma coisa boa), outros dados críticos de segurança parecem ser ignorados quando se trata de limpá-lo da memory.

Estou pensando, por exemplo, na class PrivateKey . Considere um cenário em que você carregaria uma chave RSA privada de um arquivo PKCS # 12, usando-o para executar alguma operação. Agora, neste caso, farejar a senha por si só não ajudaria muito, desde que o access físico ao arquivo de chave seja adequadamente restrito. Como um invasor, você ficaria muito melhor se obtivesse a chave diretamente em vez da senha. A informação desejada pode ser vazada múltiplas, core dumps, uma session de depurador ou arquivos de swap são apenas alguns exemplos.

E, como se constata, não há nada que permita limpar as informações particulares de um PrivateKey da memory, porque não há API que permita limpar os bytes que formam as informações correspondentes.

Esta é uma situação ruim, já que este artigo descreve como esta circunstância poderia ser potencialmente explorada.

A biblioteca OpenSSL, por exemplo, sobrescreve seções críticas de memory antes que chaves privadas sejam liberadas. Como o Java é coletor de lixo, precisaríamos de methods explícitos para limpar e invalidar informações particulares de chaves Java, que devem ser aplicadas imediatamente após o uso da chave.

Como Jon Skeet afirma, não há como usar a reflection.

No entanto, se a reflection é uma opção para você, você pode fazer isso.

 public static void main(String[] args) { System.out.println("please enter a password"); // don't actually do this, this is an example only. Scanner in = new Scanner(System.in); String password = in.nextLine(); usePassword(password); clearString(password); System.out.println("password: '" + password + "'"); } private static void usePassword(String password) { } private static void clearString(String password) { try { Field value = String.class.getDeclaredField("value"); value.setAccessible(true); char[] chars = (char[]) value.get(password); Arrays.fill(chars, '*'); } catch (Exception e) { throw new AssertionError(e); } } 

quando correr

 please enter a password hello world password: '***********' 

Nota: se o caractere String [] foi copiado como parte de um ciclo de GC, existe uma chance de a cópia anterior estar em algum lugar na memory.

Essa cópia antiga não apareceria em um dump de heap, mas se você tiver access direto à memory bruta do processo, poderá vê-la. Em geral, você deve evitar que alguém tenha esse access.

Edit: Voltando a esta resposta após um ano de pesquisa de segurança, eu percebo que faz a implicação bastante infeliz que você iria realmente comparar senhas de texto simples. Por favor não. Use um hash unidirecional seguro com um sal e um número razoável de iterações . Considere o uso de uma biblioteca: esse material é difícil de acertar!

Resposta original: E quanto ao fato de que String.equals () usa avaliação de curto-circuito e, portanto, é vulnerável a um ataque de temporização? Pode ser improvável, mas você poderia, teoricamente, cronometrar a comparação de senhas para determinar a seqüência correta de caracteres.

 public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; // Quits here if Strings are different lengths. if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; // Quits here at first different character. while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; } 

Mais alguns resources sobre ataques de temporização:

  • Uma lição em ataques temporais
  • Uma discussão sobre os ataques de temporização sobre o Stack Exchange de Segurança da Informação
  • E, claro, a página da Wikipedia Timing Attack

Estas são todas as razões, deve-se escolher o array char [] em vez de String para senha.

1. Como Strings são imutáveis ​​em Java, se você armazenar a senha como texto simples, ela estará disponível na memory até que Garbage collector a apague e, como String é usada no pool de String para reutilização, há grandes chances de permanecer na memory por muito tempo duração, o que representa uma ameaça à segurança. Como qualquer pessoa que tenha access ao despejo de memory pode encontrar a senha em texto não criptografado e essa é outra razão pela qual você deve sempre usar uma senha criptografada em vez de texto sem formatação. Como Strings são imutáveis, não há como o conteúdo de Strings ser alterado, porque qualquer alteração produzirá uma nova String, enquanto se char [] você ainda pode definir todos os elementos como em branco ou zero. Portanto, armazenar a senha na matriz de caracteres reduz claramente o risco de roubo de senha.

2. O próprio Java recomenda o uso do método getPassword () do JPasswordField, que retorna um método char [] e obsoleto getText (), que retorna a senha em texto claro indicando o motivo da segurança. É bom seguir os conselhos da equipe de Java e aderir ao padrão, em vez de ir contra.

3. Com String, há sempre o risco de imprimir texto simples no arquivo de log ou no console, mas se usar o Array, você não imprimirá o conteúdo do array, ao contrário, o local da memory será impresso. embora não seja uma razão real, mas ainda faz sentido.

  String strPassword="Unknown"; char[] charPassword= new char[]{'U','n','k','w','o','n'}; System.out.println("String password: " + strPassword); System.out.println("Character password: " + charPassword); String password: Unknown Character password: [C@110b053 

Referência de: http://javarevisited.blogspot.com/2012/03/why-character-array-is-better-than.html Espero que isso ajude.

Não há nada que o array char forneça a você, String, a menos que você o limpe manualmente após o uso, e eu não vi ninguém realmente fazendo isso. Então para mim a preferência de char [] vs String é um pouco exagerada.

Dê uma olhada na amplamente utilizada biblioteca Spring Security aqui e pergunte a si mesmo – as senhas incompetentes ou char [] do Spring Security não fazem muito sentido. Quando algum hacker desagradável consome memory da RAM, tenha certeza de que ela conseguirá todas as senhas, mesmo que você use meios sofisticados de ocultá-las.

No entanto, Java muda o tempo todo, e alguns resources assustadores, como o recurso de Deduplicação de Cadeia do Java 8, podem incorporar objects String sem o seu conhecimento. Mas essa conversa é diferente.

Strings are immutable and cannot be altered once they have been created. Creating a password as a string will leave stray references to the password on the heap or on the String pool. Now if someone takes a heap dump of the Java process and carefully scans through he might be able to guess the passwords. Of course these non used strings will be garbage collected but that depends on when the GC kicks in.

On the other side char[] are mutable as soon as the authentication is done you can overwrite them with any character like all M’s or backslashes. Now even if someone takes a heap dump he might not be able to get the passwords which are not currently in use. This gives you more control in the sense like clearing the Object content yourself vs waiting for the GC to do it.

The short and straightforward answer would be because char[] is mutable while String objects are not.

Strings in Java are immutable objects. That is why they can’t be modified once created, and therefore the only way for their contents to be removed from memory is to have them garbage collected. It will be only then when the memory freed by the object can be overwritten, and the data will be gone.

Now garbage collection in Java doesn’t happen at any guaranteed interval. The String can thus persist in memory for a long time, and if a process crashes during this time, the contents of the string may end up in a memory dump or some log.

With a character array , you can read the password, finish working with it as soon as you can, and then immediately change the contents.

1) Since Strings are immutable in Java if you store the password as plain text, it will be available in memory until Garbage collector clears it and since String is used in String pool for reusability, there is pretty high chance that it will remain in memory for the long duration, which poses a security threat. Since anyone who has access to memory dump can find the password in clear text and that’s another reason you should always use an encrypted password than plain text. Since Strings are immutable, there are no way contents of Strings can be changed because any change will produce new String, while if you char[] you can still set all his element as blank or zero. So Storing password in character array mitigates security risk of stealing the password.

2) Java itself recommends using getPassword() method of JPasswordField which returns a char[] and deprecated getText() method which returns password in clear text stating security reason. It’s good to follow advice from Java team and adhering to standard rather than going against it.

String in java is immutable. So whenever a string is created, it will remain in the memory until it is garbage collected. So anyone who has access to the memory can read the value of the string.
If the value of the string is modified then it will end up creating a new string. So both the original value and the modified value stay in the memory until it is garbage collected.

With the character array, the contents of the array can be modified or erased once the purpose of the password is served. The original contents of the array will not be found in memory after it is modified and even before the garbage collection kicks in.

Because of the security concern it is better to store password as a character array.

String is immutable and it goes to the string pool. Once written, it cannot be overwritten.

char[] is an array which you should overwrite once you used the password and this is how it should be done:

 char[] passw = request.getPassword().toCharArray() if (comparePasswords(dbPassword, passw) { allowUser = true; cleanPassword(passw); cleanPassword(dbPassword); passw=null; } private static void cleanPassword (char[] pass) { for (char ch: pass) { ch = null; } } 

One scenario where the attacker could use it is a crashdump – when the JVM crashes and generates a memory dump – you will be able to see the password.

That is not necessarily a malicious external attacker. This could be a support user that has access to the server for monitoring purposes. He could peek into a crashdump and find the passwords.