Статьи

Spring Security: предотвращение атаки методом перебора

Spring Security может многое сделать для вас.

Блокировка аккаунта, соль пароля. Но как насчет блокировщика грубой силы.

Это то, что вы должны сделать сами.

К счастью, Spring — достаточно гибкая структура, поэтому его не составляет особого труда.

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

Прежде всего, вы должны включить springSecurityEventListener в вашем config.groovy

1
grails.plugins.springsecurity.useSecurityEventListener = true

затем реализовать слушателей
в / src / bruteforce создавать классы

01
02
03
04
05
06
07
08
09
10
11
12
13
/**
Registers all failed attempts to login. Main purpose to count attempts for particular account ant block user
 
*/
class AuthenticationFailureListener implements ApplicationListener {
 
    LoginAttemptCacheService loginAttemptCacheService
 
    @Override
    void onApplicationEvent(AuthenticationFailureBadCredentialsEvent e) {
        loginAttemptCacheService.failLogin(e.authentication.name)
    }
}

Далее мы должны создать слушатель для успешных входов
в той же упаковке

01
02
03
04
05
06
07
08
09
10
11
12
/**
 Listener for successfull logins. Used for reseting number on unsuccessfull logins for specific account
*/
class AuthenticationSuccessEventListener implements ApplicationListener{
 
    LoginAttemptCacheService loginAttemptCacheService
 
    @Override
    void onApplicationEvent(AuthenticationSuccessEvent e) {
        loginAttemptCacheService.loginSuccess(e.authentication.name)
    }
}

Мы не помещали их в нашу папку grails-app, поэтому нам нужно преобразовать эти классы как весенние бобы.
Добавьте следующие строки в grails-app / conf / spring / resources.groovy

1
2
3
4
5
6
7
8
9
beans = {
    authenticationFailureListener(AuthenticationFailureListener) {
        loginAttemptCacheService = ref('loginAttemptCacheService')
    }
 
    authenticationSuccessEventListener(AuthenticationSuccessEventListener) {
        loginAttemptCacheService = ref('loginAttemptCacheService')
    }
}

Вы, вероятно, заметили использование LoginAttemptCacheService loginAttemptCacheService
Давайте реализуем это. Это был бы типичный сервис Grails

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package com.picsel.officeanywhere
 
import com.google.common.cache.CacheBuilder
import com.google.common.cache.CacheLoader
import com.google.common.cache.LoadingCache
 
import java.util.concurrent.TimeUnit
import org.apache.commons.lang.math.NumberUtils
import javax.annotation.PostConstruct
 
class LoginAttemptCacheService {
 
    private LoadingCache
               attempts;
    private int allowedNumberOfAttempts
    def grailsApplication
 
    @PostConstruct
    void init() {
        allowedNumberOfAttempts = grailsApplication.config.brutforce.loginAttempts.allowedNumberOfAttempts
        int time = grailsApplication.config.brutforce.loginAttempts.time
 
        log.info 'account block configured for $time minutes'
        attempts = CacheBuilder.newBuilder()
                   .expireAfterWrite(time, TimeUnit.MINUTES)
                   .build({0} as CacheLoader);
    }
 
    /**
     * Triggers on each unsuccessful login attempt and increases number of attempts in local accumulator
     * @param login - username which is trying to login
     * @return
     */
    def failLogin(String login) {
        def numberOfAttempts = attempts.get(login)
        log.debug 'fail login $login previous number for attempts $numberOfAttempts'
        numberOfAttempts++
 
        if (numberOfAttempts > allowedNumberOfAttempts) {
            blockUser(login)
            attempts.invalidate(login)
        } else {
            attempts.put(login, numberOfAttempts)
        }
    }
 
    /**
     * Triggers on each successful login attempt and resets number of attempts in local accumulator
     * @param login - username which is login
     */
    def loginSuccess(String login) {
        log.debug 'successfull login for $login'
        attempts.invalidate(login)
    }
 
    /**
     * Disable user account so it would not able to login
     * @param login - username that has to be disabled
     */
    private void blockUser(String login) {
        log.debug 'blocking user: $login'
        def user = User.findByUsername(login)
        if (user) {
            user.accountLocked = true;
            user.save(flush: true)
        }
    }
}

Мы будем использовать CacheBuilder из библиотеки Google Guava. Так что добавьте следующую строку в BuildConfig.groovy

1
2
3
dependencies {
    runtime 'com.google.guava:guava:11.0.1'
    }

И последний шаг — добавление конфигурации сервиса в cinfig.groovy.

1
2
3
4
5
brutforce {
    loginAttempts {
        time = 5
        allowedNumberOfAttempts = 3
    }

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

Подробнее о весенних событиях безопасности
Подробнее о кэшировании с помощью Google Guava

Пользователь Grails может просто использовать этот плагин https://github.com/grygoriy/bruteforcedefender

Приятного кодирования и не забудьте поделиться!

Ссылка: Предотвратите атаку грубой силы с помощью Spring Security от нашего партнера по JCG Григория Михалюно в блоге Григория Михалюно .