Каков наилучший способ для реализации вызываемого в Java, который требует постоянного времени для завершения - PullRequest
4 голосов
/ 11 февраля 2012

Для защиты от перебора паролей и защиты от временных атак, пытающихся обнаружить действительные имена пользователей, я хочу, чтобы процесс входа в систему занимал постоянное количество времени независимо от успешной аутентификации или нет. Короче говоря, я всегда хочу, чтобы процесс входа в систему занимал, скажем, 1000 мс (предположим, что любой результат занимает доли этого времени), независимо от результата. Каков наилучший способ сделать это на Java? Это мое нынешнее мышление:

class ConstantDuration<T> implements Callable<T> {

 ConstantDuration(Callable<T> task,long duration) {
  this.task = task;
  this.duration = duration;
 }

 public T call() throws Exception {
  long start = System.currentTimeMillis();
  long elapsed = 0;
  T result = null;
  try {
   result = task.call();
  } finally {
    elapsed = System.currentTimeMillis() - start;
  }
  if ( elapsed < duration ) {
   Thread.sleep(duration-elapsed);
  }
  return result;
 }
}

Ответы [ 4 ]

2 голосов
/ 11 февраля 2012

Если вы хотите, чтобы каждая попытка входа в систему занимала какое-то минимальное время, и в то же время вы хотите обрабатывать большее количество пользователей (например, 1000 входов в секунду), то нет способа справиться с этим с помощью простого " пусть поток подождет некоторое время »- вы, в основном, сами выполняете DOS, поскольку в Java каждый поток занимает некоторые ресурсы, и, таким образом, общее количество потоков ограничено, даже если они на самом деле ничего не делают.

Чтобы оставаться масштабируемым, вы должны использовать асинхронный ввод-вывод, чтобы принимать запросы на вход от пользователей, проверять пароль (возможно, также асинхронно), устанавливать таймер, когда следует повторно проверять соединение этого пользователя, а затем отвечать (также асинхронно) , К сожалению, Java делает это довольно сложным для записи (а затем и для чтения).

2 голосов
/ 11 февраля 2012

Очевидно, что вы захотите перевести спящий режим в блок finally, поэтому это также происходит, если задача выдает исключение (например, PasswordExpiredException?).

Другая проблема заключается в обработке случая elapsed > duration.Вы говорите, что этого никогда не происходит, но уверены ли вы?Что если ваш запрос к базе данных будет заблокирован блокировкой?Предполагая, что вы хотите запретить аутентификацию в таком случае, вы можете сделать:

ExecutorService exec = Executors.newFixedThreadPool(10);

<T> T doInConstantTime(Callable<T> task, long millis, T defaultResponse) {
    Future<T> future = exec.submit(task);
    Thread.sleep(millis);
    if (future.isDone()) {
        return future.get();
    } else {
        future.cancel(false); // or true? 
        return defaultResponse;
    }
}

(Конечно, вам нужно будет добавить правильную обработку исключений)

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

0 голосов
/ 11 февраля 2012

Сон внутри вашей темы будет означать, что она не сможет ничего сделать.Это не будет масштабироваться.

Используйте запланированного исполнителя и запланируйте отложенную задачу, которая будет возвращать результат в будущем.http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/ScheduledExecutorService.html

0 голосов
/ 11 февраля 2012

Как вы собираетесь сократить вызов result = task.call (); если оно превышает 1000 мс? Все, что вам нужно, - это функция, которая не возвращает до времени t1, независимо от того, выполнено выполнение или нет. Простым решением было бы использование sleep или await в исполняющем потоке после отправки задачи исполнителю пула потоков. И затем проверьте, является ли future.isDone (). Если нет, отмените, если да, вы уже ждали 1000 мс.

...