Перейти к основному содержимому

Путеводитель по Пассаю

· 11 мин. чтения

1. Введение

В настоящее время большинство веб-приложений имеют свою политику паролей, которая, проще говоря, создана для того, чтобы заставить пользователей создавать пароли, которые трудно взломать.

Чтобы сгенерировать такие пароли или проверить их, мы можем использовать библиотеку Passay .

2. Зависимость от Maven

Если мы хотим использовать библиотеку Passay в нашем проекте, необходимо добавить следующую зависимость в наш pom.xml :

<dependency>
<groupId>org.passay</groupId>
<artifactId>passay</artifactId>
<version>1.3.1</version>
</dependency>

Мы можем найти его здесь .

3. Проверка пароля

Проверка пароля — одна из двух основных функций, предоставляемых библиотекой Passay. Это легко и интуитивно понятно. Давайте обнаружим это.

3.1. PasswordData

Чтобы проверить наш пароль, мы должны использовать PasswordData. Это контейнер для информации, необходимой для проверки. Он может хранить такие данные, как:

  • пароль
  • имя пользователя
  • список ссылок на пароли
  • источник

Свойства пароля и имени пользователя объясняются сами собой. Библиотека Passay дает нам HistoricalReference и SourceReference , которые мы можем добавить в список ссылок на пароли.

Мы можем использовать поле происхождения для хранения информации о том, был ли пароль сгенерирован или определен пользователем.

3.2. PasswordValidator

Мы должны знать, что нам нужны объекты PasswordData и PasswordValidator , чтобы начать проверку паролей. Мы уже обсуждали PasswordData . Давайте создадим PasswordValidator сейчас.

Во-первых, мы должны определить набор правил для проверки пароля. Мы должны передать их конструктору при создании объекта PasswordValidator :

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

Есть два способа передать наш пароль объекту PasswordData . Мы передаем его либо конструктору, либо методу установки:

PasswordData passwordData = new PasswordData("1234");

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

Мы можем проверить наш пароль, вызвав метод validate() в PasswordValidator :

RuleResult validate = passwordValidator.validate(passwordData);

В результате мы получим объект RuleResult .

3.3. ПравилоРезультат

RuleResult содержит интересную информацию о процессе проверки. Это происходит в результате метода validate() .

Прежде всего, он может сказать нам, действителен ли пароль:

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

Кроме того, мы можем узнать, какие ошибки возвращаются, когда пароль недействителен. Коды ошибок и описания проверки хранятся в RuleResultDetail :

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"));

Наконец, мы можем изучить метаданные проверки пароля с помощью RuleResultMetadata :

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

4. Генерация пароля

Помимо проверки, библиотека Passay позволяет нам генерировать пароли. Мы можем указать правила, которые должен использовать генератор.

Чтобы сгенерировать пароль, нам нужен объект PasswordGenerator . Получив его, мы вызываем метод generatePassword() и передаем список CharacterRules . Вот пример кода:

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"));

Мы должны знать, что нам нужен объект CharacterData для создания CharacterRule . Еще одним интересным фактом является то, что библиотека предоставляет нам EnglishCharacterData. Это перечисление из пяти наборов символов:

  • цифры
  • строчные буквы английского алфавита
  • заглавные буквы английского алфавита
  • сочетание строчных и прописных наборов
  • специальные символы

Однако ничто не может помешать нам определить наш набор символов. Это так же просто, как реализовать интерфейс CharacterData . Давайте посмотрим, как мы можем это сделать:

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

@Override
public String getCharacters() {
return "ABCxyz123!@#";
}
});

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

Assert.assertTrue(containsOnlyCharactersFromSet(password, "ABCxyz123!@#"));

5. Положительные правила сопоставления

Мы уже узнали, как мы можем генерировать и проверять пароли. Для этого нам нужно определить набор правил. По этой причине мы должны знать, что в Passay доступно два типа правил : правила положительного соответствия и правила отрицательного соответствия. ``

Во-первых, давайте выясним, что такое положительные правила и как мы можем их использовать.

Положительные правила соответствия принимают пароли, которые содержат предоставленные символы, регулярные выражения или соответствуют некоторым ограничениям.

Существует шесть правил положительного соответствия:

  • AllowedCharacterRule — определяет все символы, которые должен содержать пароль.
  • AllowedRegexRule — определяет регулярное выражение, которому должен соответствовать пароль
  • CharacterRule — определяет набор символов и минимальное количество символов, которые должны быть включены в пароль.
  • LengthRule — определяет минимальную длину пароля.
  • CharacterCharacteristicsRule — проверяет, соответствует ли пароль N заданным правилам.
  • LengthComplexityRule — позволяет нам определять разные правила для разных длин паролей.

5.1. Простые правила положительного сопоставления

Теперь мы рассмотрим все правила с простой конфигурацией. Они определяют набор допустимых символов или шаблонов или приемлемую длину пароля.

Вот краткий пример обсуждаемых правил:

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

Мы видим, что каждое правило дает нам четкое объяснение, если пароль недействителен. Есть уведомления о том, что пароль слишком короткий и содержит два недопустимых символа. Мы также можем заметить, что пароль не соответствует предоставленному регулярному выражению.

Более того, нам сообщают, что в нем недостаточно строчных букв.

5.2. ПерсонажХарактеристикиПравило

CharcterCharacterisitcsRule является более сложным, чем правила, представленные ранее. Чтобы создать объект CharcterCharacterisitcsRule , нам нужно предоставить список CharacterRule s. Более того, мы должны установить, скольким из них должен совпадать пароль. Мы можем сделать это следующим образом:

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

Представленное CharacterCharacteristicsRule требует, чтобы пароль содержал три из четырех предоставленных правил.

5.3. ДлинаСложностьПравило

С другой стороны, библиотека Passay предоставляет нам LengthComplexityRule . Это позволяет нам определить, какие правила следует применять к паролю какой длины. В отличие от CharacterCharacteristicsRule , они позволяют нам использовать все виды правил, а не только CharacterRule .

Давайте проанализируем пример:

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

Как мы видим, для пароля, содержащего от одного до пяти символов, мы применяем CharacterRule . Но для пароля, содержащего от шести до десяти символов, мы хотим, чтобы пароль соответствовал AllowedCharacterRule .

6. Правила отрицательного сопоставления

В отличие от правил положительного сопоставления, правила отрицательного сопоставления отклоняют пароли, содержащие предоставленные символы, регулярные выражения, записи и т. д.

Давайте выясним, что такое отрицательные правила соответствия:

  • IllegalCharacterRule — определяет все символы, которые не должны содержаться в пароле.
  • IllegalRegexRule – определяет регулярное выражение, которое не должно совпадать
  • IllegalSequenceRule — проверяет, содержит ли пароль недопустимую последовательность символов.
  • NumberRangeRule – определяет диапазон цифр, который не должен содержать пароль.
  • WhitespaceRule — проверяет, содержит ли пароль пробелы
  • DictionaryRule — проверяет, равен ли пароль какой-либо записи словаря
  • DictionarySubstringRule — проверяет, содержит ли пароль словарную запись.
  • HistoryRule — проверяет, содержит ли пароль какую-либо историческую ссылку на пароль.
  • DigestHistoryRule — проверяет, содержит ли пароль какую-либо ссылку на переработанный исторический пароль.
  • SourceRule — проверяет, содержит ли пароль какую-либо ссылку на исходный пароль.
  • DigestSourceRule — проверяет, содержит ли пароль какую-либо ссылку на дайджест-источник пароля.
  • UsernameRule — проверяет, содержит ли пароль имя пользователя
  • RepeatCharacterRegexRule — проверяет, содержит ли пароль повторяющиеся символы ASCII .

6.1. Простые правила отрицательного сопоставления

Во-первых, мы увидим, как мы можем использовать простые правила, такие как IllegalCharacterRule , IllegalRegexRule и т. д . Вот короткий пример:

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

Пример показывает нам, как работают описанные правила. Подобно положительным правилам сопоставления, они дают нам полную обратную связь о проверке.

6.2. Словарь правил

Что, если мы хотим проверить, не равен ли пароль предоставленным словам.

По этой причине библиотека Passay дает нам отличные инструменты для этого. Давайте обнаружим DictionaryRule и DictionarySubstringRule :

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

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

Мы видим, что правила словаря позволяют нам предоставить список запрещенных слов. Полезно, когда у нас есть список наиболее распространенных или простых для взлома паролей. Поэтому разумно запретить пользователям их использовать.

В реальной жизни мы бы наверняка загрузили список слов из текстового файла или базы данных. В этом случае мы можем использовать WordLists . Он имеет три перегруженных метода, которые принимают массив Reader и создают ArrayWordList .

6.3. HistoryRule и SourceRule

Кроме того, библиотека Passay дает нам HistoryRule и SourceRule . Они могут проверять пароли по историческим паролям или текстовому содержимому из различных источников.

Давайте посмотрим на пример:

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 помогает нам проверить, использовался ли пароль ранее. Поскольку такие методы небезопасны, мы не хотим, чтобы пользователи использовали старые пароли.

С другой стороны, SourceRule позволяет нам проверить, отличается ли пароль от тех, которые указаны в SourceReferences . Мы можем избежать риска использования одних и тех же паролей в разных системах или приложениях.

Стоит отметить, что существуют такие правила, как DigestSourceRule и DigestHistoryRule. Мы рассмотрим их в следующем параграфе.

6.4. Правила дайджеста

В библиотеке Passay есть два правила дайджеста : DigestHistoryRule и DigestSourceRule . Правила дайджеста предназначены для работы с паролями, хранящимися в виде дайджеста или хэша. Следовательно, чтобы определить их, нам нужно предоставить объект EncodingHashBean .

Давайте посмотрим, как это делается:

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

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

На этот раз мы создаем HistoricalReference по метке и зашифрованному паролю к конструктору. После этого мы создали экземпляр EncodingHashBean с правильным кодеком и алгоритмом дайджеста.

Кроме того, мы можем указать количество итераций и наличие соли в алгоритме.

Когда у нас есть bean-компонент кодирования, мы можем проверить наш дайджест-пароль:

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

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

RuleResult validate = passwordValidator.validate(passwordData);

Assert.assertTrue(validate.isValid());

Мы можем узнать больше о EncodingHashinBean на веб-странице библиотеки Cryptacular .

6.5. RepeatCharacterRegexRule

Еще одно интересное правило проверки — RepeatCharacterRegexRule . Мы можем использовать его, чтобы проверить, содержит ли пароль повторяющиеся символы ASCII .

Вот пример кода:

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 . Это позволяет запретить использование имени пользователя в пароле.

Как мы узнали ранее, мы должны хранить имя пользователя в PasswordData :

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. Индивидуальные сообщения

Библиотека Passay позволяет нам настраивать сообщения, возвращаемые правилами проверки. Во-первых, мы должны определить сообщения и назначить им коды ошибок.

Мы можем поместить их в простой файл. Давайте посмотрим, насколько это просто:

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

Когда у нас есть сообщения, мы должны загрузить этот файл. Наконец, мы можем передать его в объект PasswordValidator .

Вот пример кода:

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

MessageResolver resolver = new PropertiesMessageResolver(props);

Как видим, мы загрузили файл message.properties и передали его в объект Properties . Затем мы можем использовать объект Properties для создания PropertiesMessageResolver .

Давайте посмотрим на примере, как использовать преобразователь сообщений:

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

Пример ясно показывает, что мы можем преобразовать все коды ошибок с помощью валидатора, оснащенного распознавателем сообщений.

8. Заключение

В этом уроке мы узнали, как использовать библиотеку Passay . Мы проанализировали несколько примеров того, как можно легко использовать библиотеку для проверки пароля. Предоставленные правила охватывают большинство распространенных способов обеспечения безопасности пароля.

Но мы должны помнить, что сама библиотека Passay не делает наш пароль безопасным. Во-первых, мы должны узнать, что такое общие правила, а затем использовать библиотеку для их реализации.

Все примеры, как всегда, можно найти на GitHub .