RustFS RPC signature verification logs shared secret
Description
RustFS is a distributed object storage system built in Rust. From >= 1.0.0-alpha.1 to 1.0.0-alpha.79, invalid RPC signatures cause the server to log the shared HMAC secret (and expected signature), which exposes the secret to log readers and enables forged RPC calls. In crates/ecstore/src/rpc/http_auth.rs, the invalid signature branch logs sensitive data. This log line includes secret and expected_signature, both derived from the shared HMAC key. Any invalidly signed request triggers this path. The function is reachable from RPC and admin request handlers. This vulnerability is fixed in 1.0.0-alpha.80.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
rustfscrates.io | >= 1.0.0-alpha.1, < 1.0.0-alpha.80 | 1.0.0-alpha.80 |
Affected products
1Patches
16b2eebee1d07fix: Remove secret and signature from the log (#1466)
3 files changed · +75 −12
crates/ecstore/src/rpc/http_auth.rs+8 −3 modified@@ -108,14 +108,19 @@ pub fn verify_rpc_signature(url: &str, method: &Method, headers: &HeaderMap) -> } // Generate expected signature - let expected_signature = generate_signature(&secret, url, method, timestamp); // Compare signatures if signature != expected_signature { error!( - "verify_rpc_signature: Invalid signature: secret {}, url {}, method {}, timestamp {}, signature {}, expected_signature {}", - secret, url, method, timestamp, signature, expected_signature + "verify_rpc_signature: Invalid signature: url {}, method {}, timestamp {}, signature {}, expected_signature: {}***{}|{}", + url, + method, + timestamp, + signature, + expected_signature.chars().next().unwrap_or('*'), + expected_signature.chars().last().unwrap_or('*'), + expected_signature.len() ); return Err(std::io::Error::other("Invalid signature"));
rustfs/src/admin/mod.rs+0 −2 modified@@ -239,14 +239,12 @@ pub fn make_admin_route(console_enabled: bool) -> std::io::Result<impl S3Route> )?; // Performance profiling endpoints (available on all platforms, with platform-specific responses) - #[cfg(not(target_os = "windows"))] r.insert( Method::GET, format!("{}{}", ADMIN_PREFIX, "/debug/pprof/profile").as_str(), AdminOperation(&handlers::ProfileHandler {}), )?; - #[cfg(not(target_os = "windows"))] r.insert( Method::GET, format!("{}{}", ADMIN_PREFIX, "/debug/pprof/status").as_str(),
rustfs/src/config/mod.rs+67 −7 modified@@ -47,7 +47,7 @@ const LONG_VERSION: &str = concat!( concat!("git status :\n", build::GIT_STATUS_FILE), ); -#[derive(Debug, Parser, Clone)] +#[derive(Parser, Clone)] #[command(version = SHORT_VERSION, long_version = LONG_VERSION)] pub struct Opt { /// DIR points to a directory on a filesystem. @@ -60,7 +60,11 @@ pub struct Opt { pub volumes: Vec<String>, /// bind to a specific ADDRESS:PORT, ADDRESS can be an IP or hostname - #[arg(long, default_value_t = rustfs_config::DEFAULT_ADDRESS.to_string(), env = "RUSTFS_ADDRESS")] + #[arg( + long, + default_value_t = rustfs_config::DEFAULT_ADDRESS.to_string(), + env = "RUSTFS_ADDRESS" + )] pub address: String, /// Domain name used for virtual-hosted-style requests. @@ -73,23 +77,43 @@ pub struct Opt { pub server_domains: Vec<String>, /// Access key used for authentication. - #[arg(long, default_value_t = rustfs_credentials::DEFAULT_ACCESS_KEY.to_string(), env = "RUSTFS_ACCESS_KEY")] + #[arg( + long, + default_value_t = rustfs_credentials::DEFAULT_ACCESS_KEY.to_string(), + env = "RUSTFS_ACCESS_KEY" + )] pub access_key: String, /// Secret key used for authentication. - #[arg(long, default_value_t = rustfs_credentials::DEFAULT_SECRET_KEY.to_string(), env = "RUSTFS_SECRET_KEY")] + #[arg( + long, + default_value_t = rustfs_credentials::DEFAULT_SECRET_KEY.to_string(), + env = "RUSTFS_SECRET_KEY" + )] pub secret_key: String, /// Enable console server - #[arg(long, default_value_t = rustfs_config::DEFAULT_CONSOLE_ENABLE, env = "RUSTFS_CONSOLE_ENABLE")] + #[arg( + long, + default_value_t = rustfs_config::DEFAULT_CONSOLE_ENABLE, + env = "RUSTFS_CONSOLE_ENABLE" + )] pub console_enable: bool, /// Console server bind address - #[arg(long, default_value_t = rustfs_config::DEFAULT_CONSOLE_ADDRESS.to_string(), env = "RUSTFS_CONSOLE_ADDRESS")] + #[arg( + long, + default_value_t = rustfs_config::DEFAULT_CONSOLE_ADDRESS.to_string(), + env = "RUSTFS_CONSOLE_ADDRESS" + )] pub console_address: String, /// Observability endpoint for trace, metrics and logs,only support grpc mode. - #[arg(long, default_value_t = rustfs_config::DEFAULT_OBS_ENDPOINT.to_string(), env = "RUSTFS_OBS_ENDPOINT")] + #[arg( + long, + default_value_t = rustfs_config::DEFAULT_OBS_ENDPOINT.to_string(), + env = "RUSTFS_OBS_ENDPOINT" + )] pub obs_endpoint: String, /// tls path for rustfs API and console. @@ -137,6 +161,42 @@ pub struct Opt { pub buffer_profile: String, } +impl std::fmt::Debug for Opt { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Opt") + .field("volumes", &self.volumes) + .field("address", &self.address) + .field("server_domains", &self.server_domains) + .field("access_key", &self.access_key) + .field("secret_key", &Opt::mask_sensitive(Some(&self.secret_key))) // Hide sensitive values + .field("console_enable", &self.console_enable) + .field("console_address", &self.console_address) + .field("obs_endpoint", &self.obs_endpoint) + .field("tls_path", &self.tls_path) + .field("license", &Opt::mask_sensitive(self.license.as_ref())) + .field("region", &self.region) + .field("kms_enable", &self.kms_enable) + .field("kms_backend", &self.kms_backend) + .field("kms_key_dir", &self.kms_key_dir) + .field("kms_vault_address", &self.kms_vault_address) + .field("kms_vault_token", &Opt::mask_sensitive(self.kms_vault_token.as_ref())) + .field("kms_default_key_id", &self.kms_default_key_id) + .field("buffer_profile_disable", &self.buffer_profile_disable) + .field("buffer_profile", &self.buffer_profile) + .finish() + } +} + +impl Opt { + /// Mask sensitive information in Option<String> + fn mask_sensitive(s: Option<&String>) -> String { + match s { + None => "".to_string(), + Some(s) => format!("{}***{}|{}", s.chars().next().unwrap_or('*'), s.chars().last().unwrap_or('*'), s.len()), + } + } +} + // lazy_static::lazy_static! { // pub(crate) static ref OPT: OnceLock<Opt> = OnceLock::new(); // }
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-333v-68xh-8mmqghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-22782ghsaADVISORY
- github.com/rustfs/rustfs/blob/9e162b6e9ebb874cc1d06a7b33bc4a05786578aa/crates/ecstore/src/rpc/http_auth.rsghsax_refsource_MISCWEB
- github.com/rustfs/rustfs/commit/6b2eebee1d07399ef02c0863bd515b4412a5a560ghsax_refsource_MISCWEB
- github.com/rustfs/rustfs/security/advisories/GHSA-333v-68xh-8mmqghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.