Guia de Passay

Guia de Passay

1. Introdução

Atualmente, a maioria dos aplicativos da Web tem sua política de senhas - que é, simplesmente, criada para forçar os usuários a criar senhas difíceis de quebrar.

Para gerar essas senhas ou validá-las, podemos fazer uso dePassay library.

2. Dependência do Maven

Se quisermos usar a biblioteca Passay em nosso projeto, é necessário adicionar a seguinte dependência ao nossopom.xml:


    org.passay
    passay
    1.3.1

Podemos encontrá-lohere.

3. Validação de senha

A validação de senha é uma das duas principais funcionalidades fornecidas pela biblioteca Passay. É fácil e intuitivo. Vamos descobrir.

3.1. PasswordData

Para validar nossa senha, devemos usarPasswordData.It’s a container for information that is necessary for validation.. Ele pode armazenar dados como:

  • senha

  • nome do usuário

  • lista de referências de senha

  • origem

As propriedades de senha e nome de usuário se explicam. A biblioteca Passay nos forneceHistoricalReferenceeSourceReference que podemos adicionar à lista de referências de senha.

Podemos usar o campo de origem para armazenar informações sobre se a senha foi gerada ou definida por um usuário.

3.2. PasswordValidator

We should know that we need PasswordData and PasswordValidator objects to start validating passwords. Já discutimosPasswordData. Vamos criarPasswordValidator agora.

Primeiramente, devemos definir um conjunto de regras para validação de senha. Temos que passá-los para o construtor ao criar um objetoPasswordValidator:

PasswordValidator passwordValidator = new PasswordValidator(new LengthRule(5));

Existem duas maneiras de passar nossa senha para um objetoPasswordData. Passamos para o método construtor ou setter:

PasswordData passwordData = new PasswordData("1234");

PasswordData passwordData2 = new PasswordData();
passwordData.setPassword("1234");

Podemos validar nossa senha chamando o métodovalidate() emPasswordValidator:

RuleResult validate = passwordValidator.validate(passwordData);

Como resultado, obteremos um objetoRuleResult.

3.3. RuleResult

RuleResult contém informações interessantes sobre um processo de validação. Ele vem como resultado do métodovalidate().

Primeiro de tudo, ele pode nos dizer se a senha é válida:

Assert.assertEquals(false, validate.isValid());

Moreover, we can learn what errors are returned when the password is invalid. Códigos de erro e descrições de validação são mantidos emRuleResultDetail:

RuleResultDetail ruleResultDetail = validate.getDetails().get(0);
Assert.assertEquals("TOO_SHORT", ruleResultDetail.getErrorCode());
Assert.assertEquals(5, ruleResultDetail.getParameters().get("minimumLength"));
Assert.assertEquals(5, ruleResultDetail.getParameters().get("maximumLength"));

Finalmente, podemos explorar os metadados de validação de senha comRuleResultMetadata:

Integer lengthCount = validate
  .getMetadata()
  .getCounts()
  .get(RuleResultMetadata.CountCategory.Length);
Assert.assertEquals(Integer.valueOf(4), lengthCount);

4. Geração de senha

Além da validação, a bibliotecaPassay nos permite gerar senhas. We can provide rules which the generator should use.

Para gerar uma senha, precisamos ter um objetoPasswordGenerator. Assim que o tivermos, chamamos o métodogeneratePassword()e a lista de aprovação deCharacterRules. Aqui está um código de exemplo:

CharacterRule digits = new CharacterRule(EnglishCharacterData.Digit);

PasswordGenerator passwordGenerator = new PasswordGenerator();
String password = passwordGenerator.generatePassword(10, digits);

Assert.assertTrue(password.length() == 10);
Assert.assertTrue(containsOnlyCharactersFromSet(password, "0123456789"));

Devemos saber que precisamos de um objeto deCharacterData para criarCharacterRule. Another interesting fact is that the library provides us with EnglishCharacterData. É um enum de cinco conjuntos de caracteres:

  • dígitos

  • alfabeto inglês minúsculo

  • alfabeto Inglês em maiúsculas

  • combinação de conjuntos em minúsculas e maiúsculas

  • caracteres especiais

However, nothing can stop us from defining our set of characters. É tão simples quanto implementar a interfaceCharacterData. Vamos ver como podemos fazer isso:

CharacterRule specialCharacterRule = new CharacterRule(new CharacterData() {
    @Override
    public String getErrorCode() {
        return "SAMPLE_ERROR_CODE";
    }

    @Override
    public String getCharacters() {
        return "[email protected]#";
    }
});

PasswordGenerator passwordGenerator = new PasswordGenerator();
String password = passwordGenerator.generatePassword(10, specialCharacterRule);

Assert.assertTrue(containsOnlyCharactersFromSet(password, "[email protected]#"));

5. Regras de correspondência positiva

Já aprendemos como podemos gerar e validar senhas. Para fazer isso, precisamos definir um conjunto de regras. For that reason, we should know that there are two types of rules available in Passay: positive matching rules and negative matching rules.

Em primeiro lugar, vamos descobrir quais são as regras positivas e como podemos usá-las.

Regras de correspondência positiva aceitam senhas que contêm caracteres fornecidos, expressões regulares ou se encaixam em algumas limitações.

Existem seis regras de correspondência positiva:

  • AllowedCharacterRule - define todos os caracteres que a senha deve incluir

  • AllowedRegexRule - define uma expressão regular que a senha deve corresponder

  • CharacterRule - define um conjunto de caracteres e um número mínimo de caracteres que devem ser incluídos na senha

  • LengthRule - define um comprimento mínimo da senha

  • CharacterCharacteristicsRule - verifica se a senha cumpreN das regras definidas.

  • LengthComplexityRule - nos permite definir regras diferentes para comprimentos de senha diferentes

5.1. Regras simples de correspondência positiva

Agora, vamos cobrir todas as regras que têm uma configuração simples. They define a set of legal characters or patterns or an acceptable password’s length.

Aqui está um pequeno exemplo das regras discutidas:

PasswordValidator passwordValidator = new PasswordValidator(
  new AllowedCharacterRule(new char[] { 'a', 'b', 'c' }),
  new CharacterRule(EnglishCharacterData.LowerCase, 5),
  new LengthRule(8, 10)
);

RuleResult validate = passwordValidator.validate(new PasswordData("12abc"));

assertFalse(validate.isValid());
assertEquals(
  "ALLOWED_CHAR:{illegalCharacter=1, matchBehavior=contains}",
  getDetail(validate, 0));
assertEquals(
  "ALLOWED_CHAR:{illegalCharacter=2, matchBehavior=contains}",
  getDetail(validate, 1));
assertEquals(
  "TOO_SHORT:{minimumLength=8, maximumLength=10}",
  getDetail(validate, 4));

We can see that each rule gives us a clear explanation if the password is not valid. Existem notificações de que a senha é muito curta e possui dois caracteres ilegais. Também podemos notar que a senha não corresponde à expressão regular fornecida.

Além disso, fomos informados de que ele contém letras minúsculas insuficientes.

5.2. CharacterCharacterisitcsRule

CharcterCharacterisitcsRule é mais complexo do que as regras apresentadas antes. To create a CharcterCharacterisitcsRule object, we need to provide a list of CharacterRules. Além do mais, temos que definir a quantos deles a senha deve corresponder. Podemos fazer assim:

CharacterCharacteristicsRule characterCharacteristicsRule = new CharacterCharacteristicsRule(
  3,
  new CharacterRule(EnglishCharacterData.LowerCase, 5),
  new CharacterRule(EnglishCharacterData.UpperCase, 5),
  new CharacterRule(EnglishCharacterData.Digit),
  new CharacterRule(EnglishCharacterData.Special)
);

ApresentadoCharacterCharacteristicsRule requer uma senha para conter três das quatro regras fornecidas.

5.3. LengthComplexityRule

Por outro lado, a bibliotecaPassay nos forneceLengthComplexityRule. It allows us to define which rules should be applied to the password of which length. Em contraste comCharacterCharacteristicsRule, eles nos permitem usar todos os tipos de regras - não apenasCharacterRule.

Vamos analisar o exemplo:

LengthComplexityRule lengthComplexityRule = new LengthComplexityRule();
lengthComplexityRule.addRules("[1,5]", new CharacterRule(EnglishCharacterData.LowerCase, 5));
lengthComplexityRule.addRules("[6,10]",
  new AllowedCharacterRule(new char[] { 'a', 'b', 'c', 'd' }));

Como podemos ver para senhas de um a cinco caracteres, aplicamosCharacterRule. Mas para uma senha contendo de seis a dez caracteres, queremos que a senha corresponda aAllowedCharacterRule.

6. Regras de correspondência negativa

Ao contrário das regras de correspondência positiva, as regras de correspondência negativa rejeitam senhas que contenham caracteres fornecidos, expressões regulares, entradas, etc.

Vamos descobrir quais são as regras de correspondência negativa:

  • IllegalCharacterRule - define todos os caracteres que uma senha não deve conter

  • IllegalRegexRule - define uma expressão regular que não deve corresponder

  • IllegalSequenceRule - verifica se uma senha tem uma sequência ilegal de caracteres

  • NumberRangeRule - define um intervalo de números que uma senha não deve conter

  • WhitespaceRule - verifica se uma senha contém espaços em branco

  • DictionaryRule - verifica se uma senha é igual a qualquer registro do dicionário

  • DictionarySubstringRule - verifica se uma senha contém algum registro de dicionário

  • HistoryRule - verifica se uma senha contém qualquer referência de senha histórica

  • DigestHistoryRule - verifica se uma senha contém qualquer referência de senha histórica digerida

  • SourceRule - verifica se uma senha contém qualquer referência de senha de origem

  • DigestSourceRule - verifica se uma senha contém qualquer referência de senha de fonte de resumo

  • UsernameRule - verifica se uma senha contém um nome de usuário

  • RepeatCharacterRegexRule - verifica se uma senha contém caracteresASCII repetidos

6.1. Regras simples de correspondência negativa

Em primeiro lugar, veremos como podemos usar regras simples, comoIllegalCharacterRule,IllegalRegexRule, etc. Aqui está um pequeno exemplo:

PasswordValidator passwordValidator = new PasswordValidator(
  new IllegalCharacterRule(new char[] { 'a' }),
  new NumberRangeRule(1, 10),
  new WhitespaceRule()
);

RuleResult validate = passwordValidator.validate(new PasswordData("abcd22 "));

assertFalse(validate.isValid());
assertEquals(
  "ILLEGAL_CHAR:{illegalCharacter=a, matchBehavior=contains}",
  getDetail(validate, 0));
assertEquals(
  "ILLEGAL_NUMBER_RANGE:{number=2, matchBehavior=contains}",
  getDetail(validate, 4));
assertEquals(
  "ILLEGAL_WHITESPACE:{whitespaceCharacter= , matchBehavior=contains}",
  getDetail(validate, 5));

O exemplo mostra como as regras descritas funcionam. Da mesma forma que as regras de correspondência positiva, elas nos fornecem um feedback completo sobre a validação.

6.2. Regras do dicionário

E se quisermos verificar se uma senha não é igual às palavras fornecidas.

Por esse motivo, a bibliotecaPassay nos oferece excelentes ferramentas para isso. Vamos descobrirDictionaryRule eDictionarySubstringRule:

WordListDictionary wordListDictionary = new WordListDictionary(
  new ArrayWordList(new String[] { "bar", "foobar" }));

DictionaryRule dictionaryRule = new DictionaryRule(wordListDictionary);
DictionarySubstringRule dictionarySubstringRule = new DictionarySubstringRule(wordListDictionary);

We can see dictionary rules enable us to provide a list of banned words. É benéfico quando temos uma lista das senhas mais comuns ou fáceis de quebrar. Portanto, é razoável proibir os usuários de usá-los.

Na vida real, certamente carregaríamos uma lista de palavras de um arquivo de texto ou de um banco de dados. Nesse caso, podemos usarWordLists. Ele possui três métodos sobrecarregados que usam uma matriz deReaders e criamArrayWordList.

6.3. HistoryRule eSourceRule

Além disso, a bibliotecaPassay nos dáHistoryRuleeSourceRule. Eles podem validar senhas com senhas históricas ou conteúdo de texto de várias fontes.

Vejamos o exemplo:

SourceRule sourceRule = new SourceRule();
HistoryRule historyRule = new HistoryRule();

PasswordData passwordData = new PasswordData("123");
passwordData.setPasswordReferences(
  new PasswordData.SourceReference("source", "password"),
  new PasswordData.HistoricalReference("12345")
);

PasswordValidator passwordValidator = new PasswordValidator(
  historyRule, sourceRule);

HistoryRules nos ajuda a verificar se uma senha foi usada antes. Como essas práticas são inseguras, não queremos que os usuários usem senhas antigas.

Por outro lado,SourceRule nos permite verificar se a senha é diferente das fornecidas emSourceReferences. Podemos evitar o risco de ter as mesmas senhas em diferentes sistemas ou aplicativos.

Vale a pena mencionar que existem regras comoDigestSourceRuleeDigestHistoryRule.. Iremos cobri-las no próximo parágrafo.

6.4. Regras do resumo

Existem duas regras de resumo na bibliotecaPassay:DigestHistoryRuleeDigestSourceRule. Digest rules are intended to work with passwords stored as digest or hash. Portanto, para defini-los, precisamos fornecer um objetoEncodingHashBean.

Vamos ver como isso é feito:

List historicalReferences = Arrays.asList(
  new PasswordData.HistoricalReference(
    "SHA256",
    "2e4551de804e27aacf20f9df5be3e8cd384ed64488b21ab079fb58e8c90068ab"
));

EncodingHashBean encodingHashBean = new EncodingHashBean(
  new CodecSpec("Base64"),
  new DigestSpec("SHA256"),
  1,
  false
);

Desta vez, criamosHistoricalReference por um rótulo e a senha codificada para o construtor. Depois disso, instanciamosEncodingHashBean com o Codec e o algoritmo de resumo adequados.

Além disso, podemos especificar o número de iterações e se o algoritmo é salgado.

Uma vez que temos um bean de codificação, podemos validar nossa senha de resumo:

PasswordData passwordData = new PasswordData("example!");
passwordData.setPasswordReferences(historicalReferences);

PasswordValidator passwordValidator = new PasswordValidator(new DigestHistoryRule(encodingHashBean));

RuleResult validate = passwordValidator.validate(passwordData);

Assert.assertTrue(validate.isValid());

Podemos aprender mais sobreEncodingHashinBean emCryptacular library webpage.

6.5. RepeatCharacterRegexRule

Outra regra de validação interessante éRepeatCharacterRegexRule. We can use it to check whether password contains repeating ASCII characters.

Aqui está um código de amostra:

PasswordValidator passwordValidator = new PasswordValidator(new RepeatCharacterRegexRule(3));

RuleResult validate = passwordValidator.validate(new PasswordData("aaabbb"));

assertFalse(validate.isValid());
assertEquals("ILLEGAL_MATCH:{match=aaa, pattern=([^\\x00-\\x1F])\\1{2}}", getDetail(validate, 0));

6.6. UsernameRule

A última regra que vamos discutir neste capítulo éUsernameRule. It enables us to prohibit using the user’s name in the password. 

Como aprendemos antes, devemos armazenar o nome de usuário emPasswordData:

PasswordValidator passwordValidator = new PasswordValidator(new UsernameRule());

PasswordData passwordData = new PasswordData("testuser1234");
passwordData.setUsername("testuser");

RuleResult validate = passwordValidator.validate(passwordData);

assertFalse(validate.isValid());
assertEquals("ILLEGAL_USERNAME:{username=testuser, matchBehavior=contains}", getDetail(validate, 0));

7. Mensagens Personalizadas

A bibliotecaPassay nos permite personalizar as mensagens retornadas pelas regras de validação. Firstly, we should define the messages and assign them to error codes.

Podemos colocá-los em um arquivo simples. Vamos ver como é fácil:

TOO_LONG=Password must not have more characters than %2$s.
TOO_SHORT=Password must not contain less characters than %2$s.

Depois que tivermos mensagens, precisamos carregar esse arquivo. Finalmente, podemos passá-lo para o objetoPasswordValidator.

Aqui está um código de exemplo:

URL resource = this.getClass().getClassLoader().getResource("messages.properties");
Properties props = new Properties();
props.load(new FileInputStream(resource.getPath()));

MessageResolver resolver = new PropertiesMessageResolver(props);

Como podemos ver, carregamos o arquivomessage.propertiese o passamos para o objetoProperties. Então, podemos usar o objetoProperties para criarPropertiesMessageResolver.

Vamos dar uma olhada no exemplo de como usar o resolvedor de mensagens:

PasswordValidator validator = new PasswordValidator(
  resolver,
  new LengthRule(8, 16),
  new WhitespaceRule()
);

RuleResult tooShort = validator.validate(new PasswordData("XXXX"));
RuleResult tooLong = validator.validate(new PasswordData("ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"));

Assert.assertEquals(
  "Password must not contain less characters than 16.",
  validator.getMessages(tooShort).get(0));
Assert.assertEquals(
  "Password must not have more characters than 16.",
  validator.getMessages(tooLong).get(0));

O exemplo mostra claramente que podemos traduzir todos os códigos de erro com o validador equipado com um resolvedor de mensagens.

8. Conclusão

Neste tutorial, aprendemos como usar a bibliotecaPassay. Analisamos vários exemplos de como a biblioteca pode ser facilmente usada para validação de senha. As regras fornecidas abrangem a maioria das formas comuns de garantir que uma senha seja segura.

Mas devemos lembrar que a biblioteca do Passay em si não torna nossa senha segura. Primeiro, devemos aprender o que são regras gerais e depois usar a biblioteca para implementá-las.

Todos os exemplos, como sempre, podem ser encontradosover on GitHub.