Strings são imutáveis . Isso significa que, depois de criar String
, se outro processo puder despejar memória, não há como (além da reflexão ) você poder se livrar dos dados antes que a coleta de lixo comece .
Com uma matriz, você pode limpar explicitamente os dados depois de terminar. Você pode sobrescrever a matriz com o que quiser e a senha não estará presente em nenhum lugar do sistema, mesmo antes da coleta de lixo.
Portanto, sim, essa é uma preocupação de segurança - mas mesmo o uso char[]
reduz apenas a janela de oportunidade para um invasor, e é apenas para esse tipo específico de ataque.
Conforme observado nos comentários, é possível que as matrizes que estão sendo movidas pelo coletor de lixo deixem cópias dispersas dos dados na memória. Acredito que isso seja específico da implementação - o coletor de lixo pode limpar toda a memória, para evitar esse tipo de coisa. Mesmo que isso aconteça, ainda há o tempo durante o qual os char[]
caracteres reais são exibidos como uma janela de ataque.
char[]
locais corta essa linha de ataque, algo que não é possível ao usarString
.Embora outras sugestões aqui pareçam válidas, há outro bom motivo. Com simplicidade,
String
você tem chances muito maiores de imprimir acidentalmente a senha em logs , monitores ou em algum outro local inseguro.char[]
é menos vulnerável.Considere isto:
Impressões:
fonte
toString
éclassname@hashcode
.[C
representachar[]
, o resto é código hash hexadecimal.Password
tipo de classe para isso. É menos obscuro e mais difícil de passar acidentalmente para algum lugar.Para citar um documento oficial, o guia Java Cryptography Architecture diz isso sobre
char[]
vs.String
senhas (sobre criptografia baseada em senha, mas geralmente é sobre senhas, é claro):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 log):
fonte
String
é muito bem compreendido e como ele se comporta na JVM ... existem boas razões para usarchar[]
no lugar deString
quando lidar com senhas de maneira segura.At which point
- isso é específico do aplicativo, mas a regra geral é fazê-lo assim que você obtém algo que deveria ser uma senha (texto simples ou não). Você o obtém do navegador como parte de uma solicitação HTTP, por exemplo. Você não pode controlar a entrega, mas pode controlar seu próprio armazenamento; assim que obtê-lo, coloque-o em um caractere [], faça o que você precisa fazer com ele, defina tudo como '0' e deixe o gc recupere-o.As matrizes de caracteres (
char[]
) podem ser limpas após o uso, definindo cada caractere como zero e Strings, não. Se alguém puder ver a imagem da memória de alguma forma, poderá ver uma senha em texto sem formatação se Strings forem usadas, mas sechar[]
for usada, após limpar os dados com zeros, a senha estará segura.fonte
HttpServletRequest
objeto em texto sem formatação. Se a versão da JVM for 1.6 ou inferior, ela estará no espaço permitido. Se estiver na versão 1.7, ainda será legível até que seja coletado. (Toda vez que é.)intern()
cordas arbitrárias sem pensar . Mas você está certo de que asString
instâncias existem em primeiro lugar (até serem coletadas) e transformá-las emchar[]
matrizes depois não as altera.new String()
ouStringBuilder.toString()
eu gerenciei aplicativos com muitas constantes de strings e tivemos muitos permgen creep como resultado. Até 1.7.intern()
uma string arbitrária pode causar a alocação de uma string equivalente no espaço permgen. O último poderia obter GC'ed, se não houvesse uma seqüência literal do mesmo conteúdo compartilhando esse objeto ...Algumas pessoas acreditam que você precisa sobrescrever a memória usada para armazenar a senha quando não precisar mais dela. Isso reduz a janela de tempo que um invasor precisa ler a senha do seu sistema e ignora completamente o fato de que o invasor já precisa de acesso suficiente para seqüestrar a memória da JVM para fazer isso. Um invasor com tanto acesso pode capturar seus principais eventos, tornando isso completamente inútil (AFAIK, por favor, corrija-me se estiver errado).
Atualizar
Graças aos comentários, tenho que atualizar minha resposta. Aparentemente, existem dois casos em que isso pode adicionar uma (muito) pequena melhoria de segurança, pois reduz o tempo que uma senha pode pousar no disco rígido. Ainda acho que é um exagero para a maioria dos casos de uso.
Se possível, desativar os dumps principais e o arquivo de troca resolveriam os dois problemas. No entanto, eles exigiriam direitos de administrador e podem reduzir a funcionalidade (menos memória para usar) e extrair RAM de um sistema em execução ainda seria uma preocupação válida.
fonte
Não acho que seja uma sugestão válida, mas pelo menos posso adivinhar o motivo.
Eu acho que a motivação é ter certeza de que você pode apagar todo o rastreamento da senha na memória imediatamente e com segurança depois que ela for usada. Com um,
char[]
você pode sobrescrever cada elemento da matriz com um espaço em branco ou algo com certeza. Você não pode editar o valor internoString
dessa maneira.Mas isso por si só não é uma boa resposta; por que não apenas garantir que uma referência
char[]
ouString
não escape? Então não há problema de segurança. Mas o fato é que osString
objetos podem serintern()
edificados em teoria e mantidos vivos dentro da piscina constante. Suponho que o usochar[]
proíba essa possibilidade.fonte
char[]
podem ser modificadas e, então, é irrelevante se elas são coletadas ou não. E como a internação de strings precisa ser feita explicitamente para não literais, é o mesmo que dizer que achar[]
pode ser referenciada por um campo estático.A resposta já foi dada, mas eu gostaria de compartilhar um problema que descobri recentemente com as bibliotecas padrão Java. Enquanto eles tomam muito cuidado agora de substituir as strings de senha por
char[]
todos os lugares (o que é bom), outros dados críticos de segurança parecem ser esquecidos quando se trata de limpá-los da memória.Estou pensando, por exemplo, na classe PrivateKey . Considere um cenário em que você carregaria uma chave RSA privada de um arquivo PKCS # 12, usando-a para executar alguma operação. Agora, nesse caso, farejar a senha por si só não ajudaria muito, desde que o acesso físico ao arquivo-chave seja adequadamente restrito. Como invasor, você seria muito melhor se obtivesse a chave diretamente em vez da senha. As informações desejadas podem ser coletor vazado, core dumps, sessão de depurador ou arquivos de troca são apenas alguns exemplos.
E, como se vê, não há nada que permita limpar as informações privadas de um
PrivateKey
da memória, porque não há API que permita limpar os bytes que formam as informações correspondentes.Esta é uma situação ruim, pois este artigo descreve como essa circunstância pode ser potencialmente explorada.
A biblioteca OpenSSL, por exemplo, substitui as seções críticas da memória antes que as chaves privadas sejam liberadas. Como o Java é coletado pelo lixo, precisaríamos de métodos explícitos para limpar e invalidar informações privadas para chaves Java, que devem ser aplicadas imediatamente após o uso da chave.
fonte
PrivateKey
que na verdade não carrega seu conteúdo privado na memória: por exemplo, por meio de um token de hardware PKCS # 11. Talvez uma implementação de software do PKCS # 11 possa cuidar da limpeza manual da memória. Talvez usar algo como a loja NSS (que compartilha a maior parte de sua implementação com oPKCS11
tipo de loja em Java) seja melhor. OKeychainStore
(armazenamento de chaves OSX) carrega o conteúdo completo da chave privada em suasPrivateKey
instâncias, mas não deve ser necessário. (Não sei o que oWINDOWS-MY
KeyStore faz no Windows.)Como Jon Skeet afirma, não há maneira, exceto usando a reflexão.
No entanto, se a reflexão é uma opção para você, você pode fazer isso.
quando correr
Nota: se o char [] da String foi copiado como parte de um ciclo do GC, é possível que a cópia anterior esteja em algum lugar na memória.
Essa cópia antiga não apareceria em um despejo de heap, mas se você tiver acesso direto à memória bruta do processo, poderá vê-la. Em geral, você deve evitar alguém que tenha esse acesso.
fonte
'***********'
.Scanner
buffer interno do e, como você não o usouSystem.console().readPassword()
, na forma legível na janela do console. Mas para os casos de uso mais práticos, a duração dausePassword
execução é o problema real. Por exemplo, ao fazer uma conexão com outra máquina, leva um tempo significativo e informa ao invasor que é o momento certo para procurar a senha no heap. A única solução é impedir que os invasores leiam a memória heap…Por todas essas razões, deve-se escolher uma matriz char [] em vez de String para uma senha.
1. Como as Strings são imutáveis em Java, se você armazenar a senha como texto sem formatação, ela estará disponível na memória até que o Garbage Collector a limpe, e como a String é usada no pool de String para reutilização, há uma chance muito grande de permanecem na memória por um longo período, o que representa uma ameaça à segurança.
Como qualquer pessoa que tenha acesso ao despejo de memória pode encontrar a senha em texto não criptografado, esse é outro motivo pelo qual você deve sempre usar uma senha criptografada em vez de texto sem formatação. Como as Strings são imutáveis, não há como o conteúdo das Strings poder ser alterado porque qualquer alteração produzirá uma nova String, enquanto que se você usar um char [], poderá definir todos os elementos como em branco ou zero. Portanto, armazenar uma senha em uma matriz de caracteres reduz claramente o risco de segurança de roubar uma senha.
2) próprio Java recomenda o uso do método getPassword () do JPasswordField, que retorna um char [], em vez do método obsoleto getText (), que retorna senhas em texto não criptografado, indicando os motivos de segurança. É bom seguir os conselhos da equipe Java e aderir aos padrões, em vez de ir contra eles.
3. Com String, há sempre o risco de imprimir texto sem formatação em um arquivo de log ou console, mas se você usar uma matriz, não imprimirá o conteúdo de uma matriz, mas seu local de memória será impresso. Embora não seja um motivo real, ainda faz sentido.
Referenciado neste blog . Eu espero que isso ajude.
fonte
Edit: Voltando a esta resposta após um ano de pesquisa de segurança, percebo que isso implica a lamentável implicação de que você realmente compararia senhas em texto simples. Por favor não. Use um hash unidirecional seguro com um sal e um número razoável de iterações . Considere usar uma biblioteca: é difícil acertar essas coisas!
Resposta original: E o fato de o String.equals () usar avaliação de curto-circuito e, portanto, é vulnerável a um ataque de tempo? Pode ser improvável, mas você poderia teoricamente cronometrar a comparação da senha para determinar a sequência correta de caracteres.
Mais alguns recursos sobre ataques de tempo:
fonte
Arrays.equals
parachar[]
é tão alta quanto paraString.equals
. Se alguém se importava, havia uma classe de chave dedicada encapsulando a senha real e cuidando dos problemas - oh espere, os pacotes de segurança reais têm classes de chave dedicadas, essas perguntas e respostas são apenas um hábito fora delas, por exemploJPasswordField
, para usarchar[]
em vez deString
(onde os algoritmos reais usam debyte[]
qualquer maneira).sleep(secureRandom.nextInt())
antes de rejeitar uma tentativa de login de qualquer maneira, isso não apenas remove a possibilidade de ataques cronometrados, mas também neutraliza as tentativas de força bruta.Não há nada que o array de caracteres dê a você vs String, a menos que você o limpe manualmente após o uso, e eu não vi ninguém fazendo isso. Então, para mim, a preferência de char [] vs String é um pouco exagerada.
Dê uma olhada na biblioteca Spring Security amplamente usada aqui e pergunte a si mesmo - os caras do Spring Security são incompetentes ou as senhas char [] simplesmente não fazem muito sentido. Quando um hacker desagradável agarra despejos de memória da sua RAM, verifique se ele obterá todas as senhas, mesmo se você usar maneiras sofisticadas de escondê-las.
No entanto, o Java muda o tempo todo e alguns recursos assustadores, como o recurso de redução de redundância de seqüência do Java 8, podem internar objetos String sem o seu conhecimento. Mas isso é uma conversa diferente.
fonte
Strings são imutáveis e não podem ser alteradas depois de criadas. Criar uma senha como uma sequência deixará referências dispersas para a senha na pilha ou no pool de Sequências. Agora, se alguém pegar um monte de lixo do processo Java e varrer cuidadosamente, poderá adivinhar as senhas. É claro que essas seqüências não usadas serão coletadas como lixo, mas isso depende de quando o GC entra em ação.
Por outro lado, char [] é mutável assim que a autenticação é concluída, você pode substituí-los por qualquer caractere como todos os M's ou barras invertidas. Agora, mesmo que alguém faça um despejo de pilha, ele pode não conseguir obter as senhas que não estão em uso no momento. Isso lhe dá mais controle no sentido de limpar o conteúdo do objeto e esperar o GC fazer isso.
fonte
strings
E oString
pool (pool de seqüências de caracteres usadas anteriormente) foram AMBOS armazenados em Permgen antes de 1.7. Além disso, consulte a seção 5.1: docs.oracle.com/javase/specs/jvms/se6/html/… A JVM sempre verificavaStrings
se eles tinham o mesmo valor de referência e ligariaString.intern()
para VOCÊ. O resultado foi que toda vez que a JVMconstant_pool
detectasse Strings idênticas no ou no heap, as moveria para permgen. E trabalhei em várias aplicações com "rastejando permgen" até 1.7. Foi um problema real.constant_pool
que estava localizado no permgen e, se uma string foi usada mais de uma vez, ela seria internada.String é imutável e vai para o pool de strings. Uma vez escrito, não pode ser substituído.
char[]
é uma matriz que você deve substituir depois de usar a senha e é assim que deve ser feito:Um cenário em que o invasor pode usá-lo é um despejo de memória - quando a JVM trava e gera um despejo de memória - você poderá ver a senha.
Isso não é necessariamente um invasor externo malicioso. Pode ser um usuário de suporte que tenha acesso ao servidor para fins de monitoramento. Ele poderia espiar um despejo de memória e encontrar as senhas.
fonte
request.getPassword()
já não cria a string e a adiciona ao pool?ch = '0'
altera a variável localch
; não tem efeito na matriz. E seu exemplo é inútil de qualquer maneira, você começa com uma instância de string que você chamatoCharArray()
, criando uma nova matriz e, mesmo que você sobrescreva a nova matriz corretamente, ela não muda a instância da string, por isso não tem vantagem em usar apenas a string instância.Arrays.fill(pass, '0');
A resposta curta e direta seria porque
char[]
é mutável enquanto osString
objetos não são.Strings
em Java são objetos imutáveis. É por isso que eles não podem ser modificados depois de criados e, portanto, a única maneira de remover o conteúdo da memória é fazer com que o lixo seja coletado. Somente então será possível substituir a memória liberada pelo objeto e os dados desaparecerão.Agora, a coleta de lixo em Java não acontece em nenhum intervalo garantido. O
String
pode, assim, persistem na memória por um longo tempo, e se um processo deixa de funcionar durante este tempo, o conteúdo da corda pode acabar em um despejo de memória ou algum log.Com uma matriz de caracteres , você pode ler a senha, terminar de trabalhar com ela o mais rápido possível e alterar imediatamente o conteúdo.
fonte
String em java é imutável. Portanto, sempre que uma string é criada, ela permanece na memória até ser coletada como lixo. Portanto, qualquer pessoa que tenha acesso à memória pode ler o valor da string.
Se o valor da string for modificado, ele acabará criando uma nova string. Portanto, o valor original e o valor modificado permanecem na memória até a coleta de lixo.
Com a matriz de caracteres, o conteúdo da matriz pode ser modificado ou apagado quando o objetivo da senha for atendido. O conteúdo original da matriz não será encontrado na memória após sua modificação e mesmo antes da coleta de lixo.
Por causa da preocupação com a segurança, é melhor armazenar a senha como uma matriz de caracteres.
fonte
É discutível se você deve usar String ou Char [] para esse fim, pois ambos têm suas vantagens e desvantagens. Depende do que o usuário precisa.
Como Strings em Java são imutáveis, sempre que algumas tentam manipular sua string, ela cria um novo Objeto e a String existente permanece inalterada. Isso pode ser visto como uma vantagem para armazenar uma senha como uma String, mas o objeto permanece na memória mesmo após o uso. Portanto, se alguém conseguiu a localização da memória do objeto, ela pode rastrear facilmente sua senha armazenada nessa localização.
Char [] é mutável, mas tem a vantagem de que após o uso, o programador pode limpar explicitamente a matriz ou substituir valores. Portanto, quando terminar de usá-lo, ele será limpo e ninguém poderá saber sobre as informações que você armazenou.
Com base nas circunstâncias acima, pode-se ter uma idéia de usar String ou de Char [] para seus requisitos.
fonte
Caso String:
Caso CHAR ARRAY:
fonte