-
Notifications
You must be signed in to change notification settings - Fork 94
Feature/improve password security #377
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/improve password security #377
Conversation
Converted SecretManager to an actor-based architecture for improved thread-safety with benchmarks
… password leak in logs
- replace String with bytes to avoid clones - use `choose()` to simplify getting random bytes
Remove content parameter from `redact_password()`. Unlike `redact_secrets()` which scans content, this function now only redacts CSPRNG-generated passwords.
Add `MAX_RETRIES` const to control password generation retry attempts when conflicts are detected
Add validation in `Password::new()` to reject passwords shorter than 8 characters with `TooShort` error.
…rate_password` fn
password conflicts are extremely rare so no need for all that verbosity logging
The `Password` type ensures that passwords cannot be empty, making explicit empty checks unnecessary
Update `redact_password()` to accept the new `Password` type instead of String
|
@abd0-omar thank u under review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good work on adding compile-time protection against password leakage using the secrecy crate. The Password newtype pattern is clean and the performance improvements using byte slices are nice.
Issues to Address
1. Collision detection logic only shuffles, doesn't regenerate
for attempt in 0..MAX_RETRIES {
if redaction_map.values().any(|v| v.as_bytes() == password) {
password.shuffle(&mut rng); // Just shuffles same chars
continue;
}
// ...
}Shuffling the same characters is unlikely to resolve a collision since you're checking exact byte match. Consider regenerating the entire password with fresh random characters instead.
2. Missing validation in Password deserialization
The Password type validates length in new(), but serde deserialization bypasses this:
#[derive(Deserialize)]
pub struct Password(SecretString); // Deserializes directlyA password shorter than 8 chars could be deserialized from JSON. Consider adding a custom deserializer:
impl<'de> Deserialize<'de> for Password {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de> {
let s = String::deserialize(deserializer)?;
Password::new(s).map_err(serde::de::Error::custom)
}
}3. Potential underflow in generate_password
If length is less than the number of required characters (4 with symbols, 3 without), this could underflow:
password.extend((0..(length - password.len())).map(...));The minimum is enforced in Password::new(), but that happens after generation. Consider validating early:
pub fn generate_password(length: usize, ...) -> Result<Password, PasswordGenerationError> {
if length < 8 {
return Err(PasswordGenerationError::TooShort);
}
// ...
}4. redact_password() behavioral change
The old function replaced password occurrences within content. The new function just returns a redaction key. This is fine, but make sure all callers are updated to handle this change correctly.
Thanks for the thorough work on this! The PR description and screenshots are helpful.
The caller `redact_and_store_secrets_impl` uses `extend()` to merge results, so returning the full cloned map caused redundancy as we already have the old mappings.
2d2992b to
71fc409
Compare
6c91c8d to
40c151a
Compare
|
Thanks for the detailed review @ahmedhesham6 ! the current fixes:
Please let me know for any changes! |
|
@abd0-omar can you sync with beta branch |
|
@ahmedhesham6 I've merged "actor-model", which is already merged with branch "beta", into the current branch |
Description
Add compile time protection against password leakage by using
Passwordtype that wrapsSecretStringfrom thesecrecycrate. This ensures passwords cannot be accidentally logged.Related Issues
Partially addresses #282 - Add protection for passwords to prevent accidental loggging
Changes Made
secrecycrate to prevent leaking passwordsPasswordtype wrapper inlibs/shared/src/models/password.rsPasswordtypegenerate_password()to returnResult<Password, PasswordGenerationError>generate_password()to use byte slices instead of String for better performanceTooShort,Conflict,NonUTF8no_symbolstoinclude_symbolsschemarsfrom0.8.22to1.1.0(withchrono04feature, even though chrono is not used)Screenshots
testing creating a password less than 8 characters



password redaction int the TUI
passwords in the secret.json
Breaking Changes
Minor breaking changes:
generate_password()now returnsResult<Password, PasswordGenerationError>instead ofStringOption<String>toOption<Password>redact_password()changed to accept&Passwordinstead of&strredact_and_store_password()acceptsPasswordinstead of&strif this got approved I'll work on the rest of the #282 issue