mirror of
https://github.com/rustdesk/rustdesk
synced 2026-04-21 13:27:19 +00:00
add brute-force protection for one-time password (#14682)
* add brute-force protection for temporary password Rotate the temporary password after repeated failed login attempts within one minute, and reset the failure window after successful authentication. Signed-off-by: 21pages <sunboeasy@gmail.com> * replace LazyLock with lazy_static Signed-off-by: 21pages <sunboeasy@gmail.com> * read temporary password after locking failure state Signed-off-by: 21pages <sunboeasy@gmail.com> * server: rotate temporary passwords after 10 consecutive failures Signed-off-by: 21pages <sunboeasy@gmail.com> * server: clarify temporary password failure counter comment Signed-off-by: 21pages <sunboeasy@gmail.com> --------- Signed-off-by: 21pages <sunboeasy@gmail.com>
This commit is contained in:
parent
0cf3e8ed40
commit
8dea347a21
1 changed files with 55 additions and 6 deletions
|
|
@ -1993,11 +1993,6 @@ impl Connection {
|
|||
constant_time_eq(&hasher2.finalize()[..], &self.lr.password[..])
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn validate_one_password(&self, password: &str) -> bool {
|
||||
self.validate_password_plain(password)
|
||||
}
|
||||
|
||||
fn validate_password_plain(&self, password: &str) -> bool {
|
||||
if password.is_empty() {
|
||||
return false;
|
||||
|
|
@ -2025,15 +2020,68 @@ impl Connection {
|
|||
self.validate_password_plain(storage)
|
||||
}
|
||||
|
||||
// This is coarse brute-force protection for the current temporary password value.
|
||||
// We only care whether the active temporary password itself was presented correctly,
|
||||
// not whether later authorization steps succeed. A successful temporary-password
|
||||
// match clears this state immediately, and the counter also resets whenever the
|
||||
// temporary password changes or is rotated.
|
||||
fn check_update_temporary_password(&self, temporary_password_success: bool) {
|
||||
const MAX_CONSECUTIVE_FAILURES: i32 = 10;
|
||||
#[derive(Default)]
|
||||
struct State {
|
||||
password: String,
|
||||
failures: i32,
|
||||
}
|
||||
lazy_static::lazy_static! {
|
||||
static ref TEMPORARY_PASSWORD_FAILURES: Mutex<State> =
|
||||
Mutex::new(State::default());
|
||||
}
|
||||
|
||||
if !password::temporary_enabled() {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut state = TEMPORARY_PASSWORD_FAILURES.lock().unwrap();
|
||||
let current_password = password::temporary_password();
|
||||
if current_password.is_empty() {
|
||||
return;
|
||||
}
|
||||
if state.password != current_password {
|
||||
state.password = current_password;
|
||||
state.failures = 0;
|
||||
}
|
||||
|
||||
if temporary_password_success {
|
||||
state.failures = 0;
|
||||
return;
|
||||
}
|
||||
state.failures += 1;
|
||||
|
||||
if state.failures < MAX_CONSECUTIVE_FAILURES {
|
||||
return;
|
||||
}
|
||||
|
||||
password::update_temporary_password();
|
||||
let new_password = password::temporary_password();
|
||||
log::warn!(
|
||||
"Temporary password rotated after too many consecutive wrong attempts: failures={}, ip={}",
|
||||
state.failures,
|
||||
self.ip,
|
||||
);
|
||||
state.password = new_password;
|
||||
state.failures = 0;
|
||||
}
|
||||
|
||||
fn validate_password(&mut self, allow_permanent_password: bool) -> bool {
|
||||
if password::temporary_enabled() {
|
||||
let password = password::temporary_password();
|
||||
if self.validate_one_password(&password) {
|
||||
if self.validate_password_plain(&password) {
|
||||
raii::AuthedConnID::update_or_insert_session(
|
||||
self.session_key(),
|
||||
Some(password),
|
||||
Some(false),
|
||||
);
|
||||
self.check_update_temporary_password(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -2406,6 +2454,7 @@ impl Connection {
|
|||
}
|
||||
if !self.validate_password(allow_logon_screen_password) {
|
||||
self.update_failure(failure, false, 0);
|
||||
self.check_update_temporary_password(false);
|
||||
if err_msg.is_empty() {
|
||||
self.send_login_error(crate::client::LOGIN_MSG_PASSWORD_WRONG)
|
||||
.await;
|
||||
|
|
|
|||
Loading…
Reference in a new issue