INCONE60 Green - Digital and green transition of small ports
Andrzej Chybicki: projekty związane z wykorzystaniem sztucznej inteligencji to znacząca część naszych projektów
Konfiguracja polityki haseł w Keycloak

Skuteczne zarządzanie hasłami jest istotnym elementem zabezpieczania kont użytkowników, a Keycloak dostarcza narzędzia do egzekwowania silnych zasad uwierzytelniania. Dzięki konfiguracji reguł haseł administratorzy mogą zadbać o zgodność poświadczeń ze standardami bezpieczeństwa, minimalizując ryzyko nieautoryzowanego dostępu. Platforma oferuje elastyczne opcje, umożliwiające definiowanie wymagań, dotyczących długości i złożoności haseł, ich ważności oraz zapobiegania ponownemu użyciu.

W tym artykule najpierw przyjrzymy się wbudowanym mechanizmom zarządzania polityką haseł w Keycloak. Następnie omówimy możliwości ich dostosowania do specyficznych wymagań.

Wbudowane polityki

Wbudowane polityki haseł w Keycloak umożliwiają administratorom egzekwowanie zasad bezpieczeństwa w celu wzmocnienia uwierzytelniania użytkowników. Poniżej znajduje się krótki opis każdej z nich:

  1. Wygasanie hasła (Expire Password) – Wymusza zmianę hasła po określonym czasie.
  2. Iteracje hashowania (Hashing Iterations) – Określa liczbę iteracji podczas hashowania hasła w celu zwiększenia bezpieczeństwa.
  3. Brak ponownego użycia (Not Recently Used) – Zapobiega ponownemu użyciu ostatnich haseł przez użytkownika.
  4. Czarna lista haseł (Password Blacklist) – Blokuje określone hasła, zwykle słabe lub powszechnie używane.
  5. Wyrażenie regularne (Regular Expression) – Pozwala wymusić niestandardowy wzorzec regex dla walidacji hasła.
  6. Minimalna długość (Minimum Length) – Ustawia minimalną liczbę znaków wymaganą w haśle.
  7. Brak nazwy użytkownika jako hasła (Not Username) – Uniemożliwia ustawienie nazwy użytkownika jako hasła.
  8. Brak adresu e-mail jako hasła (Not Email) – Zapobiega używaniu adresu e-mail jako hasła.
  9. Brak ponownego użycia w określonym czasie (Not Recently Used in Days) – Blokuje ponowne użycie hasła przez określoną liczbę dni.
  10. Nie zawiera nazwy użytkownika (Not Contains Username) – Wymusza, aby hasło nie zawierało nazwy użytkownika.
  11. Znaki specjalne (Special Characters) – Wymaga co najmniej jednego znaku specjalnego w haśle.
  12. Wielkie litery (Uppercase Characters) – Wymusza obecność co najmniej jednej wielkiej litery w haśle.
  13. Małe litery (Lowercase Characters) – Wymaga co najmniej jednej małej litery w haśle.
  14. Cyfry (Digits) – Wymaga co najmniej jednej cyfry w haśle.
  15. Maksymalny czas ważności uwierzytelnienia (Maximum Authentication Age) – Określa maksymalny czas ważności sesji przed wymuszeniem ponownego logowania.
  16. Algorytm hashowania (Hashing Algorithm) – Określa algorytm używany do szyfrowania haseł.
  17. Maksymalna długość (Maximum Length) – Definiuje maksymalną dopuszczalną długość hasła.

Implementacja niestandardowej polityki haseł przy użyciu SPI

Aby zaimplementować niestandardową politykę haseł w Keycloak, należy użyć interfejsu dostawcy usług (SPI – Service Provider Interface).

W tym przypadku definiujemy niestandardowego dostawcę polityki haseł, implementując interfejs PasswordPolicyProviderFactory.

				
					public class PasswordCustomPolicyProviderFactory implements PasswordPolicyProviderFactory {

	public static final Integer DEFAULT_VALUE = 1;
	public static final String MIN_PASSWORD_LIFETIME_ID = "minimumPasswordLifetime";

	@Override
	public String getId() {
    	return MIN_PASSWORD_LIFETIME_ID;
	}

	@Override
	public PasswordPolicyProvider create(KeycloakSession session) {
    	return new PasswordCustomPolicyProvider(session);
	}
[...]
}


				
			

Factory instancjonuje i zwraca nową instancję PasswordCustomPolicyProvider, która zawiera logikę walidacji wymuszającą minimalny czas życia hasła. Stała MIN_PASSWORD_LIFETIME_ID pełni rolę unikalnego identyfikatora tej niestandardowej polityki, a stała DEFAULT_VALUE określa domyślny minimalny czas życia hasła (w dniach), jeśli nie zostanie skonfigurowana inna wartość w Admin Console.

				
					public class PasswordCustomPolicyProvider implements PasswordPolicyProvider {
np.
   private static final String POLICY_VIOLATION_MESSAGE = "passwordLifetimeViolation";


   private final KeycloakSession keycloakSession;

   public PasswordCustomPolicyProvider(KeycloakSession keycloakSession) {
   	this.keycloakSession = keycloakSession;
   }


   @Override
   public PolicyError validate(RealmModel realm, UserModel user, String password) {
   	PasswordCredentialProvider credentialProvider = new PasswordCredentialProvider(keycloakSession);
   	PasswordCredentialModel credentialModel = credentialProvider.getPassword(realm, user);

   	if (credentialModel == null) {
       	return null;
   	}

   	long passwordCreationTime = credentialModel.getCreatedDate();
   	long currentTime = Time.currentTimeMillis();
   	long elapsedTime = currentTime - passwordCreationTime;

   	PasswordPolicy passwordPolicy = realm.getPasswordPolicy();
   	int minPasswordLifetimeDays = passwordPolicy.getPolicyConfig(PasswordCustomPolicyProviderFactory.MIN_PASSWORD_LIFETIME_ID);
   	long minPasswordLifetimeMillis = TimeUnit.DAYS.toMillis(minPasswordLifetimeDays);
   	return elapsedTime >= minPasswordLifetimeMillis ? null : new PolicyError(POLICY_VIOLATION_MESSAGE, minPasswordLifetimeDays);
   }
[...]
}

				
			

PasswordCredentialProvider może uzyskać dostęp do zapisanego znacznika czasu utworzenia hasła za pośrednictwem instancji PasswordCredentialModel. Następnie oblicza elapsedTime jako różnicę między tym znacznikiem a bieżącym czasem systemowym, co określa, jak długo hasło jest już używane.

Następnie obiekt PasswordPolicy pobiera politykę haseł dla danego realm’u, wyodrębnia minimalny wymagany czas życia hasła w dniach (minPasswordLifetimeDays) i przelicza go na milisekundy (minPasswordLifetimeMillis). Polityka ta zapewnia, że hasło było używane przez co najmniej wymagany okres. Jeśli warunek ten nie zostanie spełniony, zwracany jest obiekt PolicyError. Klucz wiadomości o błędzie jest zapisany w stałej POLICY_VIOLATION_MESSAGE, a jego treść może być dostosowana w naszym motywie. Pozwala to na zdefiniowanie przyjaznego komunikatu, który informuje użytkownika, dlaczego zmiana hasła jest niedostępna i ile czasu pozostało do możliwości ustawienia nowego.

W ten sposób możemy definiować niestandardowe polityki haseł w Keycloak, gdy domyślny zestaw polityk okazuje się niewystarczający dla konkretnych wymagań. Taka elastyczność umożliwia bardziej szczegółową kontrolę nad uwierzytelnianiem użytkowników i zarządzaniem hasłami, gdy zachodzi taka potrzeba.

Dostosowanie interfejsu w celu poprawy UX

Domyślnie Keycloak wyświetla niespełnione polityki haseł osobno na stronie logowania. Może to być problematyczne dla wielu użytkowników, zwłaszcza gdy naruszonych jest kilka zasad jednocześnie. Prowadzi to do przeładowanego interfejsu i utrudnia użytkownikom zrozumienie wszystkich wymagań dotyczących hasła. Aby temu zaradzić, można dostosować ekran logowania tak, aby prezentował zbiorczą listę wszystkich niespełnionych polityk haseł, co zapewni bardziej przejrzyste i przyjazne dla użytkownika doświadczenie.

				
					public class CustomFreeMarkerLoginFormsProvider extends FreeMarkerLoginFormsProvider {
/**
* Mapping between password policy provider IDs and custom messages
* Note: contains only standard policies that must be displayed in the UI
*/
private final Map<String, String> policyPropertyMessages = Map.of(
LengthPasswordPolicyProviderFactory.ID, MINIMUM_LENGTH_MESSAGE,
MaximumLengthPasswordPolicyProviderFactory.ID, MAXIMUM_LENGTH_MESSAGE,
DigitsPasswordPolicyProviderFactory.ID, MINIMUM_DIGIT_MESSAGE,
SpecialCharsPasswordPolicyProviderFactory.ID, MINIMUM_SPECIAL_CHAR_MESSAGE,
UpperCasePasswordPolicyProviderFactory.ID, MINIMUM_UPPERCASE_MESSAGE,
LowerCasePasswordPolicyProviderFactory.ID, MINIMUM_LOWERCASE_MESSAGE,
NotUsernamePasswordPolicyProviderFactory.ID, NOT_USERNAME_MESSAGE,
NotContainsUsernamePasswordPolicyProviderFactory.ID, NOT_CONTAINS_USERNAME_MESSAGE,
NotEmailPasswordPolicyProviderFactory.ID, NOT_EMAIL_MESSAGE
);

[...]

@Override
protected void createCommonAttributes(Theme theme, Locale locale, Properties messagesBundle,
UriBuilder baseUriBuilder, LoginFormsPages page) {
super.createCommonAttributes(theme, locale, messagesBundle, baseUriBuilder, page);
if (realm != null && realm.getPasswordPolicy() != null) {
attributes.put("passwordPolicies", getPasswordPolicyMessages(realm.getPasswordPolicy(), messagesBundle));
}}

[...]

private Map<String, String> getPasswordPolicyMessages(PasswordPolicy passwordPolicy, Properties messagesBundle) {
Map<String, String> policyMessages = new HashMap<>();
PasswordPolicy.Builder builder = passwordPolicy.toBuilder();
for (String policyName : passwordPolicy.getPolicies()) {
var value = builder.get(policyName);
String message = extractPolicyMessage(policyName, value, messagesBundle);
if (message != null) {
policyMessages.put(policyName, message);
}
}
return policyMessages;
}

[...]

/**
* Extracts a message for a given password policy from the messages bundle
* Note: Policy message is constructed by replacing the {0} placeholder with the policy value
*/
private String extractPolicyMessage(String policy, String value, Properties messagesBundle) {
String property = policyPropertyMessages.get(policy);
if (property == null) {
return null;
}
String policyMessage = messagesBundle.getProperty(property);
return policyMessage != null ? policyMessage.replace("{0}", value) : null;
}

				
			

Funkcja getPasswordPolicyMessages() już zbiera polityki haseł z obiektu PasswordPolicy i mapuje je na odpowiednie komunikaty z pliku wiadomości (message bundle). Można ją rozszerzyć tak, aby wyświetlała wszystkie niespełnione zasady w jednej zbiorczej wiadomości.

Polityki haseł, takie jak minimalna długość, wymagane cyfry, znaki specjalne itp., są mapowane na komunikaty za pomocą metody extractPolicyMessage(). Nasza implementacja serwisu przechodzi przez każdą z zasad i sprawdza, czy jest spełniona. Jeśli nie – wyświetlany jest odpowiadający jej komunikat.

Na stronie update-password.ftl możesz zaprezentować te niespełnione zasady jako listę przy użyciu szablonów FreeMarker.

				
					
    	<#if passwordPolicies?has_content>
        	<div class="${properties.kcAlertClass}">
            	<div class="${properties.kcAlertIconWrapperClass}">
                	<span class="${properties.kcAlertIconClass}"></span>
            	</div>
            	<span class="${properties.kcAlertTitleClass}">
            	${msg("passwordInstruction")} <br>
            	<#list passwordPolicies?keys as key>
                	<span class="${properties.kcAlertTitleClass}">&#x2022; ${passwordPolicies[key]}</span><br/>
            	</#list>
            	</span>
        	</div>
    	</#if>


				
			

Praktyczne przykłady polityk haseł

Zobaczmy, jak wyglądają polityki haseł w dużych firmach.

 

Apple wymaga, aby hasła miały co najmniej osiem znaków i zawierały zarówno litery, jak i cyfry. Dodatkowo, hasła nie mogą zawierać trzech lub więcej identycznych znaków pod rząd i nie mogą być powszechnie używanymi hasłami.

 

Facebook narzuca minimalną długość hasła wynoszącą ponad sześć znaków, choć zaleca stosowanie dłuższych haseł. Choć Meta nie wymaga użycia znaków specjalnych ani cyfr, zachęca do tworzenia złożonych haseł.

 

Microsoft wymaga, aby hasła miały co najmniej 8 znaków i zawierały co najmniej dwa z następujących typów znaków: wielkie litery, małe litery, cyfry lub symbole. Dodatkowo, system może blokować możliwość ustawienia hasła zbyt podobnego do poprzedniego.

 

Chociaż firmy te korzystają z różnych narzędzi uwierzytelniania, warto zwrócić uwagę na standardy bezpieczeństwa wdrażane w dużych systemach produkcyjnych.

 

I mimo że te polityki haseł nie są skrajnie restrykcyjne, użytkownicy powinni unikać wykorzystywania w hasłach wrażliwych danych osobowych, takich jak imiona, daty urodzenia czy numery telefonów. Należy również unikać ponownego używania haseł w różnych usługach, ponieważ może to prowadzić do naruszeń bezpieczeństwa w przypadku przejęcia jednego z kont. Włączenie uwierzytelniania dwuskładnikowego (2FA) i okresowy przegląd bezpieczeństwa haseł to kolejne kroki, które użytkownicy mogą podjąć w celu zwiększenia ochrony.

Podsumowanie

Jak widać, Keycloak oferuje zestaw domyślnych polityk haseł, które obejmują standardowe zasady bezpieczeństwa, takie jak minimalna długość, wymagania dotyczące złożoności czy historia użycia haseł. Wbudowane polityki są wystarczające w wielu przypadkach, jednak w razie potrzeby istnieje możliwość ich dostosowania do konkretnych wymagań organizacyjnych. Keycloak pozwala również na tworzenie własnych polityk haseł, co daje większą kontrolę nad bezpieczeństwem.

 

Oprócz modyfikacji samych zasad, Keycloak umożliwia także dostosowanie interfejsu użytkownika. Jest to szczególnie przydatne w sytuacjach, gdy domyślny sposób prezentowania naruszeń polityk haseł — np. wyświetlanie niespełnionych wymagań osobno — nie spełnia naszych oczekiwań. W takich przypadkach możemy zmienić sposób prezentacji błędów lub wzbogacić komunikaty o dodatkowe informacje, aby były bardziej czytelne i zrozumiałe dla użytkownika.

 

Dzięki tym możliwościom Keycloak pokazuje wysoki poziom elastyczności, umożliwiając pełną kontrolę zarówno nad politykami bezpieczeństwa, jak i nad wyglądem interfejsu. Czyni go to uniwersalnym rozwiązaniem do zarządzania tożsamością i dostępem. Możliwość definiowania własnych reguł i dostosowywania komponentów sprawia, że Keycloak to skalowalne narzędzie, które z łatwością można dopasować do indywidualnych potrzeb organizacji.