java validar Por que o char[] é preferido em relação ao String para senhas?




validar char java (14)

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[] .


String é imutável e vai para o pool de strings. Uma vez escrita, não pode ser sobrescrita.

char[] é uma matriz que você deve sobrescrever uma vez que você usou a senha e é assim que deve ser feito:

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;
 }
}

Um cenário em que o invasor poderia usá-lo é um crashdump - quando a JVM falha e gera um despejo de memória - você poderá ver a senha.

Isso não é necessariamente um invasor externo mal-intencionado. Pode ser um usuário de suporte que tenha acesso ao servidor para fins de monitoramento. Ele poderia espreitar um crashdump e encontrar as senhas.


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 memória 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 objetos String podem ser intern() em teoria) e mantidos vivos dentro do pool constante. Eu suponho que usar char[] proíba essa possibilidade.


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

No entanto, se a reflexão é 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 memória.

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


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 substituir 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 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-o para executar alguma operação. Agora, neste caso, farejar a senha por si só não ajudaria muito, desde que o acesso 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 sessão 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 memória, porque não há API que permita limpar os bytes que formam as informações correspondentes.

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

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


String em java é imutável. Então, sempre que uma string é criada, ela permanecerá na memória até ser coletada como lixo. Então, 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, tanto o valor original quanto o valor modificado permanecem na memória até serem coletados como lixo.

Com a matriz de caracteres, o conteúdo da matriz pode ser modificado ou apagado depois que o objetivo da senha é exibido. O conteúdo original da matriz não será encontrado na memória depois que ele for modificado e até mesmo antes da coleta de lixo entrar em ação.

Devido à preocupação de segurança, é melhor armazenar a senha como uma matriz de caracteres.


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 recursos sobre ataques de temporização:


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 memória até que Garbage collector a apague e, como String é usada no pool de String para reutilização, há grandes chances de permanecer na memória por muito tempo duração, 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 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 memória 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: [[email protected]

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


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 memória 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.


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 objeto 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á métodos definidos que permitam alterar (sobrescrever) ou zerar o conteúdo de uma String após o uso. Esse recurso torna os objetos 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 objetos são movidos na memória 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 acesso aos arquivos de log.


A resposta curta e direta seria porque char[]é mutável enquanto os Stringobjetos não são.

Stringsem Java são objetos imutáveis. É por isso que eles não podem ser modificados depois de criados e, portanto, a única maneira de o conteúdo deles ser removido da memória é coletar lixo. Será somente quando a memória liberada pelo objeto puder ser sobrescrita e os dados desaparecerem.

Agora, a coleta de lixo em Java não acontece em nenhum intervalo garantido. O Stringpode, portanto, persistir na memória por um longo tempo, e se um processo falhar durante esse tempo, o conteúdo da seqüência de caracteres 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, em seguida, alterar imediatamente o conteúdo.


1) Como Strings são imutáveis ​​em Java se você armazenar a senha como texto simples, ela estará disponível na memória até que Garbage collector a limpe e como String é usada no pool de String para reutilização, há uma grande chance de permanecer na memória para a longa duração, 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 e essa é outra razão pela 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 de Strings ser alterado porque qualquer alteração produzirá uma nova String, enquanto se você char [] você ainda pode definir todos os seus elementos como em branco ou zero. Portanto, armazenar a senha na matriz de caracteres reduz o risco de roubar a 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 de segurança. É bom seguir o conselho da equipe de Java e aderir ao padrão, em vez de ir contra.


  1. As strings são imutáveis em Java se você armazenar a senha como texto simples, ela ficará disponível na memória 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 memória por um longo tempo. duração, o que representa uma ameaça à segurança. Desde que qualquer pessoa que tenha acesso ao despejo de memória 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 memória 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 memória assim que a autenticação for concluída.


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: [[email protected]

As cordas são imutáveis ​​e não podem ser alteradas depois de criadas. Criar uma senha como uma cadeia de caracteres deixará referências de dispersão para a senha no heap ou no conjunto de cadeias. Agora, se alguém fizer um despejo de heap do processo Java e verificar cuidadosamente, ele poderá adivinhar as senhas. É claro que essas sequências não utilizadas serão coletadas, mas isso depende de quando o GC entra em ação.

Por outro lado, char [] é mutável assim que a autenticação é feita, você pode sobrescrevê-los com qualquer caractere como todos os M's ou barras invertidas. Agora, mesmo que alguém tenha um dump de heap, ele pode não conseguir 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 ao invés de esperar que o GC o faça.





char