Play Framework, как намеренно задерживать ответ - PullRequest
0 голосов
/ 27 февраля 2020

У нас есть приложение Play, в настоящее время используется версия 2.6. Мы пытаемся предотвратить словарную атаку на наш логин, задерживая сообщение «Неудачный логин» нашим пользователям, когда они вводят неверный пароль. В настоящее время у нас есть sh и соль, и у нас есть все лучшие практики, но мы не уверены, правильно ли мы задерживаемся. Таким образом, у нас есть в нашем контроллере:

public Result login() { return ok(loginHtml) }

и у нас есть:

public Result loginAction()
{ 
  // Check for user in database
  User user = User.find.query()...

  // Was the user found?
  if (user == null) {

     // Wrong password! Delay and redirect
     Thread.sleep(10000);  <<-- how do delay correctly?
     return redirect(routes.Controller.login())
  }

  // User is not null, so all good!
  ...

}

Мы не уверены, является ли Thread.sleep(10000) лучшим способом отложить ответ, так как это может привести к зависанию другие запросы, которые приходят или используют слишком много потоков из пула по умолчанию. Мы заметили, что при скорости 80+ обращений в секунду Play Framework не перенаправляет наши HTTP-вызовы в Routes. То есть, если мы получим запрос HTTP POST, наше приложение не будет даже отправлять этот запрос на контроллер через 20 с лишним секунд, ОДНАКО в тот же период времени, если мы получим запрос HTTP GET, наше приложение обработает этот GET мгновенно. !

В настоящее время у нас есть 300 потоков в качестве минимальных / максимальных значений в наших настройках Akka для пула вилок по умолчанию. Любые идеи будут оценены. Мы запускаем t2.xlarge AWS EC2 экземпляр с Ubuntu.

Спасибо.

Ответы [ 2 ]

1 голос
/ 02 марта 2020

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

То, что вам нужно использовать, это CompletionStage / CompletableFuture или любая абстракция для работа с асинхронным c программированием и асинхронным действием.

Пожалуйста, ознакомьтесь с более подробной информацией об асинхронных ios действиях: https://www.playframework.com/documentation/2.8.x/JavaAsync

В вашем случае решение тоже будет выглядеть как-то (извините, пожалуйста, в этом могут быть ошибки - я Scala инженер-первичка):

import play.libs.concurrent.HttpExecutionContext;
import play.mvc.*;

import javax.inject.Inject;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;

public class LoginController extends Controller {

  private HttpExecutionContext httpExecutionContext;

  // Create and inject separate ScheduledExecutorService
  private ScheduledExecutorService executor; 

  @Inject
  public LoginController(HttpExecutionContext ec,
                         ScheduledExecutorService executor) {
    this.httpExecutionContext = ec;
    this.executor = executor;
  }

  public CompletionStage<Result> loginAction() {
    User user = User.find.query()...
    if (user == null) {
        return executor.schedule(() -> {redirect(routes.Controller.login());}, 10, TimeUnit.SECONDS);
    } else {
      // return another response
    }
  }
}

Надеюсь, это поможет!

0 голосов
/ 28 февраля 2020

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

В таблице User сохраните значение LocalDateTime времени последней попытки входа в систему.

Когда вы выбираете пользователя из БД, проверьте последнюю попытку время (сравните с LocalDateTime.now ()), если с момента последней попытки прошло 10 секунд, выполните сравнение пароля.

Если пароли не совпадают, сохраните время последней попытки, как сейчас.

Это также можно корректно обработать на внешнем интерфейсе, если вы предоставите хорошие сообщения об ошибках.

РЕДАКТИРОВАТЬ: Если вы хотите отложить попытки входа в систему НЕ на основе пользователя, вы можете создать таблицу попыток и сохранить последнюю попытку по IP адрес.

Если вы действительно хотите сделать свой путь, который я не рекомендую, вам нужно сначала прочитать об этом: https://www.playframework.com/documentation/2.8.x/ThreadPools

...