Skip to content

Commit d5623cb

Browse files
committed
fix(auth): avoid token match timing leak
1 parent 722c891 commit d5623cb

1 file changed

Lines changed: 11 additions & 6 deletions

File tree

  • dstack/crates/api-auth/src

dstack/crates/api-auth/src/lib.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use rocket::{
1919
Data, Request, Response, Route,
2020
};
2121
use sha2::{Digest, Sha256};
22-
use subtle::ConstantTimeEq;
22+
use subtle::{Choice, ConstantTimeEq};
2323

2424
const UNAUTH_URI: &str = "/__dstack_api_auth_unauthorized";
2525

@@ -91,9 +91,13 @@ impl Authenticator {
9191

9292
pub fn verify_token(&self, token: &str) -> bool {
9393
let candidate = sha256(token.as_bytes());
94-
self.token_hashes
94+
let matched = self
95+
.token_hashes
9596
.iter()
96-
.any(|expected| bool::from(candidate.ct_eq(expected)))
97+
.fold(Choice::from(0), |matched, expected| {
98+
matched | candidate.ct_eq(expected)
99+
});
100+
bool::from(matched)
97101
}
98102

99103
pub fn verify_basic(&self, username: &str, password: &str) -> bool {
@@ -337,15 +341,16 @@ mod tests {
337341
#[test]
338342
fn verifies_tokens_and_apache_hashes() {
339343
let dir = std::env::temp_dir().join(format!("dstack-api-auth-{}", std::process::id()));
340-
let hash = bcrypt::hash("password", 4).unwrap();
344+
let password = format!("test-password-{}", std::process::id());
345+
let hash = bcrypt::hash(&password, 4).unwrap();
341346
std::fs::write(&dir, format!("alice:{hash}\n")).unwrap();
342347
let auth = Authenticator::from_tokens(["secret"])
343348
.with_htpasswd_file(&dir)
344349
.unwrap();
345350
assert!(auth.verify_token("secret"));
346351
assert!(!auth.verify_token("wrong"));
347-
assert!(auth.verify_basic("alice", "password"));
348-
assert!(!auth.verify_basic("alice", "wrong"));
352+
assert!(auth.verify_basic("alice", &password));
353+
assert!(!auth.verify_basic("alice", &format!("{password}-wrong")));
349354
let _ = std::fs::remove_file(dir);
350355
}
351356

0 commit comments

Comments
 (0)