mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-27 16:48:58 +00:00
fix(protocols): use constant-time secret comparison in FTPS and WebDAV auth (#4403)
The FTPS and WebDAV authentication handlers compared the client-supplied secret key against the stored secret with `String::eq`, which short-circuits on the first differing byte. A network attacker who knows (or enumerates) a valid access key can recover the secret key byte-by-byte via response-timing analysis; neither path is rate limited. Switch both to a constant-time comparison using `subtle::ConstantTimeEq`, the same primitive the SFTP handler and `rustfs/src/auth.rs::constant_time_eq` already use. `subtle` is added to the `ftps` and `webdav` feature dependency sets (it was previously gated on `sftp` only). Addresses GHSA-3p3x-734c-h5vx.
This commit is contained in:
@@ -27,7 +27,7 @@ categories = ["network-programming", "filesystem"]
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
ftps = ["dep:libunftp", "dep:unftp-core", "dep:rustls", "dep:rustfs-tls-runtime"]
|
ftps = ["dep:libunftp", "dep:unftp-core", "dep:rustls", "dep:rustfs-tls-runtime", "dep:subtle"]
|
||||||
swift = [
|
swift = [
|
||||||
"dep:rustfs-keystone",
|
"dep:rustfs-keystone",
|
||||||
"dep:rustfs-ecstore",
|
"dep:rustfs-ecstore",
|
||||||
@@ -53,7 +53,7 @@ swift = [
|
|||||||
"dep:base64",
|
"dep:base64",
|
||||||
"dep:async-compression",
|
"dep:async-compression",
|
||||||
]
|
]
|
||||||
webdav = ["dep:dav-server", "dep:hyper", "dep:hyper-util", "dep:http-body-util", "dep:tokio-rustls", "dep:base64", "dep:rustls", "dep:percent-encoding", "dep:rustfs-tls-runtime"]
|
webdav = ["dep:dav-server", "dep:hyper", "dep:hyper-util", "dep:http-body-util", "dep:tokio-rustls", "dep:base64", "dep:rustls", "dep:percent-encoding", "dep:rustfs-tls-runtime", "dep:subtle"]
|
||||||
sftp = ["dep:russh", "dep:russh-sftp", "dep:uuid", "dep:subtle", "dep:tokio-util", "dep:socket2"]
|
sftp = ["dep:russh", "dep:russh-sftp", "dep:uuid", "dep:subtle", "dep:tokio-util", "dep:socket2"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ use std::net::IpAddr;
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use subtle::ConstantTimeEq;
|
||||||
use tokio::sync::broadcast;
|
use tokio::sync::broadcast;
|
||||||
use tokio::sync::watch;
|
use tokio::sync::watch;
|
||||||
use tracing::{debug, error, info, warn};
|
use tracing::{debug, error, info, warn};
|
||||||
@@ -464,7 +465,15 @@ impl Authenticator for FtpsAuthenticator {
|
|||||||
AuthenticationError::BadUser
|
AuthenticationError::BadUser
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if !identity.credentials.secret_key.eq(&s3_creds.secret_key) {
|
// Constant-time secret comparison to prevent timing side-channel
|
||||||
|
// attacks. Same primitive used by the SFTP handler and rustfs/src/auth.rs.
|
||||||
|
let secret_matches: bool = identity
|
||||||
|
.credentials
|
||||||
|
.secret_key
|
||||||
|
.as_bytes()
|
||||||
|
.ct_eq(s3_creds.secret_key.as_bytes())
|
||||||
|
.into();
|
||||||
|
if !secret_matches {
|
||||||
warn!(
|
warn!(
|
||||||
event = EVENT_FTPS_AUTH_STATE,
|
event = EVENT_FTPS_AUTH_STATE,
|
||||||
component = LOG_COMPONENT_PROTOCOLS,
|
component = LOG_COMPONENT_PROTOCOLS,
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ use std::convert::Infallible;
|
|||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use subtle::ConstantTimeEq;
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
use tokio::sync::{broadcast, watch};
|
use tokio::sync::{broadcast, watch};
|
||||||
use tokio_rustls::TlsAcceptor;
|
use tokio_rustls::TlsAcceptor;
|
||||||
@@ -441,7 +442,15 @@ where
|
|||||||
WebDavInitError::Server("User not found".to_string())
|
WebDavInitError::Server("User not found".to_string())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if !identity.credentials.secret_key.eq(&s3_creds.secret_key) {
|
// Constant-time secret comparison to prevent timing side-channel
|
||||||
|
// attacks. Same primitive used by the SFTP handler and rustfs/src/auth.rs.
|
||||||
|
let secret_matches: bool = identity
|
||||||
|
.credentials
|
||||||
|
.secret_key
|
||||||
|
.as_bytes()
|
||||||
|
.ct_eq(s3_creds.secret_key.as_bytes())
|
||||||
|
.into();
|
||||||
|
if !secret_matches {
|
||||||
warn!(
|
warn!(
|
||||||
event = EVENT_WEBDAV_AUTH_STATE,
|
event = EVENT_WEBDAV_AUTH_STATE,
|
||||||
component = LOG_COMPONENT_PROTOCOLS,
|
component = LOG_COMPONENT_PROTOCOLS,
|
||||||
|
|||||||
Reference in New Issue
Block a user