Skip to content
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

Revisit password security just in case #163

Closed
ryardley opened this issue Oct 28, 2024 · 1 comment
Closed

Revisit password security just in case #163

ryardley opened this issue Oct 28, 2024 · 1 comment

Comments

@ryardley
Copy link
Contributor

          _:warning: Potential issue_

Security improvements needed for password handling.

While the basic password handling and zeroization are good, several security enhancements are recommended:

  1. Add password strength validation
  2. Consider whether trimming passwords is appropriate for security
  3. Use constant-time comparison for password matching
  4. Add validation for empty or whitespace-only passwords

Here's a suggested implementation:

 fn get_zeroizing_pw_vec(input: Option<String>) -> Result<Zeroizing<Vec<u8>>> {
     if let Some(mut pw_str) = input {
+        if pw_str.trim().is_empty() {
+            pw_str.zeroize();
+            return Err(anyhow::anyhow!("Password cannot be empty"));
+        }
+        validate_password_strength(&pw_str)?;
         let pw = Zeroizing::new(pw_str.trim().as_bytes().to_owned());
         pw_str.zeroize();
         return Ok(pw);
     }

     // First password entry
     let mut pw_str = prompt_password("\n\nPlease enter a new password: ")?;
+    if pw_str.trim().is_empty() {
+        pw_str.zeroize();
+        return Err(anyhow::anyhow!("Password cannot be empty"));
+    }
+    validate_password_strength(&pw_str)?;
     
     // Second password entry for confirmation
     let mut confirm_pw_str = prompt_password("Please confirm your password: ")?;

     // Check if passwords match using constant-time comparison
-    if pw_str.trim() != confirm_pw_str.trim() {
+    if !constant_time_eq(pw_str.trim().as_bytes(), confirm_pw_str.trim().as_bytes()) {
         // Clean up sensitive data
         pw_str.zeroize();
         confirm_pw_str.zeroize();
         return Err(anyhow::anyhow!("Passwords do not match"));
     }

Add these helper functions:

use constant_time_eq::constant_time_eq;

fn validate_password_strength(password: &str) -> Result<()> {
    if password.len() < 12 {
        return Err(anyhow::anyhow!("Password must be at least 12 characters long"));
    }
    
    let has_uppercase = password.chars().any(|c| c.is_uppercase());
    let has_lowercase = password.chars().any(|c| c.is_lowercase());
    let has_digit = password.chars().any(|c| c.is_digit(10));
    let has_special = password.chars().any(|c| !c.is_alphanumeric());
    
    if !(has_uppercase && has_lowercase && has_digit && has_special) {
        return Err(anyhow::anyhow!(
            "Password must contain uppercase, lowercase, numbers, and special characters"
        ));
    }
    
    Ok(())
}

Originally posted by @coderabbitai[bot] in #156 (comment)

@ryardley
Copy link
Contributor Author

Tidied up zero length password issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant