Это возможно при некоторых настройках и кодировании в Spring security.
Кстати, я не предлагаю делать некоторую задержку перед подозрительными недопустимыми испытаниями входа в систему. Вы можете сделать некоторую задержку, чтобы ответить на подозрительную попытку входа в систему, но эта задержка потребует от вас приостановить поток на некоторое время в вашем приложении. Это может обеспечить DoS- или DDoS-атаку в вашей системе, если с вашим приложением одновременно происходит большое количество недопустимых входов в систему.
Лучше всего сделать быстрый ответ на подозрительный неверный логин, но в то же время приостановить учетную запись пользователя, с помощью которой пользователь пытается войти. Таким образом, предотвращение атаки методом грубой силы не приводит к обеспечению атаки Dos или DDoS.
Тем не менее, приостановка учетной записи пользователя также предоставит способ для DoS-атаки, поскольку это может привести к сбою в доставке услуги реальному пользователю. Но правильные сценарии безопасности были бы полезны в этих случаях.
Например, если обнаружена атака грубой силы, вы можете:
- показать капчу,
- приостановить действие учетной записи, электронной почты или SMS-сообщения владельца учетной записи для смены пароля
- или приостановить учетную запись на некоторое время, уведомив владельца учетной записи об изменении пароля,
- ...
Все это зависит от вашего домена и сценариев обслуживания.
Например, вы можете реализовать свой собственный UserDetailsService и обнаружить атаку методом перебора в этой реализации.
Чтобы реализовать последний сценарий Spring Security, в следующем коде объявляется менеджер аутентификации, которому передается пользовательский UserDetailsService, тип которого здесь JdbcDaoImpl (обратите внимание, что имена пакетов и запросы должны быть изменены в соответствии с вашим собственным пакетом и моделью данных).
<authentication-manager alias="authenticationManager" xmlns="http://www.springframework.org/schema/security">
<authentication-provider user-service-ref="CustomUserDetailsService" />
</authentication-manager>
<!--
This bean is to provide jdbc-user-service.
Also, it provides a way to avoid BFD along with AuthenticationFailureListener
-->
<bean id="CustomUserDetailsService" class="com.example.CustomUserDetailsService">
<property name="dataSource" ref="dataSource" />
<property name="usersByUsernameQuery" value="SELECT user_client.user_name, user_client.password, user.is_active
FROM user_client INNER JOIN user ON user_client.fk_user = user.ID
WHERE user_client.user_name=? "/>
<property name="authoritiesByUsernameQuery" value="SELECT user_client.user_name, CONCAT('ROLE_',user_client.client_id)
FROM user_client WHERE user_client.user_name=? "/>
</bean>
CustomUserDetailsService обнаруживает, происходит ли атака методом перебора или нет, вместе с AuthenticationFailureListener, который я скоро расскажу. FailedLoginCacheManager - это оболочка ehcache, которая поддерживает неудачные входы (имена пользователей) с их относительными ошибочными количествами. В кеше задано правильное времяToIdleSeconds, чтобы приостановить действие учетной записи.
public class CustomUserDetailsService extends JdbcDaoImpl {
@Autowired
private FailedLoginCacheManager cacheManager;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if (cacheManager.isBruteForceAttackLogin(username)) {
//throw some security exception
...
}
return super.loadUserByUsername(username);
}
}
Также реализован ApplicationListener для обнаружения неудачных попыток входа в систему и сохранения их внутри ehcache.
@Component public class AuthenticationFailureListener implements ApplicationListener<AuthenticationFailureBadCredentialsEvent> {
@Autowired
private FailedLoginCacheManager cacheManager;
@Override
public void onApplicationEvent(AuthenticationFailureBadCredentialsEvent event) {
UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) event.getSource();
String userName = token.getPrincipal().toString();
cacheManager.updateLoginFailureStatus(userName);
}}
Нет необходимости обсуждать больше о службе FailedLoginCacheManager, но два основных метода, которые обсуждаются здесь, могут быть реализованы, как следующие методы:
public void updateLoginFailureStatus(String userName) {
Cache cache = manager.getCache(CACHE_NAME);
Element element = cache.get(userName);
if (isValid(element)) {
int failureCount = (Integer)element.getObjectValue();
cache.remove(userName);
cache.put(new Element(userName, ++failureCount));
} else {
cache.put(new Element(userName, 1));
}
}
public boolean isBruteForceAttackLogin(String username) {
Cache cache = manager.getCache(CACHE_NAME);
Element element = cache.get(username);
if (isValid(element)) {
int failureCount = (Integer)element.getObjectValue();
return failureCount >= 3;
} else {
return false;
}
}