Rust
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Ownership and Borrowing
-
Section 4: Structuring Data
-
Section 5: Collections and Error Handling
-
Section 6: Traits and Generics
-
Section 7: Concurrency
-
Section 8: Building for the Web
-
Section 9: Memory and Performance
-
Section 10: More Standard Library and Ecosystem
-
Section 11: Advanced Rust
-
Section 12: Rust for Systems and WebAssembly
-
Section 13: Tooling and Best Practices
-
Section 14: Data Structures and Algorithms in Rust
-
Section 15: Practical Projects
-
Section 16: Interview Practice
-
Section 17: std::collections In Depth
-
Section 18: std::io and std::fs In Depth
-
Section 19: std::net
-
Section 20: std::option and std::result In Depth
-
Section 21: std::iter In Depth
-
Section 22: std::sync In Depth
-
Section 23: std::string and std::str
-
Section 24: Cargo and Crates.io Ecosystem
-
Section 25: Popular Crates Ecosystem
-
Section 26: Rust Memory Model Deep Dive
-
Section 27: More Practice Exercises
-
Section 28: More Interview Practice
-
Section 29: Async Rust Deep Dive
-
Section 30: Tokio Ecosystem In Depth
-
Section 31: Error Handling Ecosystem Deep Dive
-
Section 32: Serde In Depth
-
Section 33: Web Development with Rust Deep Dive
-
Section 34: Database Access Ecosystem
-
Section 35: Rust for Embedded Systems Deep Dive
-
Section 36: Rust Macros In Depth
-
Section 37: Advanced Trait System
-
Section 38: Unsafe Rust In Depth
-
Section 39: Rust CLI Development
-
Section 40: Testing Ecosystem Deep Dive
-
Section 41: WebAssembly Deep Dive
-
Section 42: Rust Design Patterns
-
Section 43: More Data Structures in Rust
-
Section 44: Final Practice Projects
-
Section 45: Rust Performance Optimization
-
Section 46: Rust Ecosystem Tooling
-
Section 47: More Interview and Review
205: Reading Passwords Securely
Why can't I just use std::io::stdin().read_line()?
You can, but you probably shouldn't. The problem isn't with Rust's safety guarantees, but with how terminals work. By default, terminals operate in "echo" mode. When you type a character, the terminal sends it to your program and immediately prints it back to the screen so the user can see what they're typing.
If you're building a CLI tool where a user enters a password, you don't want their secret splashed across the screen for anyone glancing over their shoulder. To fix this, you have to tell the terminal to disable echoing, read the input, and then—this is the important part—turn echoing back on so the user's terminal isn't broken for the next command they run.
How do I actually stop the terminal from echoing my typing?
Handling terminal modes manually is a nightmare because it's platform-specific. Linux and macOS use termios, while Windows does things completely differently. I wouldn't recommend writing your own wrapper for this unless you really enjoy reading C headers from the 80s.
Instead, use the rpassword crate. It's the industry standard for this in Rust. It handles the cross-platform gymnastics for you. Here is how you'd implement a simple password prompt:
[dependencies]
rpassword = "7.3"
fn main() {
println!("Enter your secret key:");
// read_password() handles disabling the echo and cleaning up afterward
let password = rpassword::read_password().expect("Failed to read password");
println!("Password received! Length: {}", password.len());
}
It's straightforward. The read_password() function blocks the thread, hides the input, and returns a String. Simple, right? Well, almost.
Once I have the password in a String, is it actually secure?
Here is where things get tricky. You now have a String on the heap. In Rust, when a String goes out of scope, the memory is freed, but it isn't necessarily zeroed. The bytes of that password might just sit in your RAM until that specific block of memory is overwritten by something else.
If an attacker manages to get a memory dump of your process, they might find that password lying around in "dead" memory. For most apps, this is an acceptable risk. But if you're building something like a password manager or a crypto wallet, it's not enough. You want to "zeroize" the memory as soon as you're done with it.
I usually suggest the secrecy crate for this. It wraps your sensitive data in a SecretString, which prevents the value from being accidentally printed to logs (via Debug) and helps ensure the memory is handled more carefully. If you're really paranoid, you can use the zeroize crate to manually overwrite the memory with zeros before the variable is dropped.
📋 Practical Task
Build a Secure CLI Vault Login
Create a small command-line program that simulates a secure login for a vault. Your program must meet the following requirements:
- Prompt the user for a username using standard
std::io::stdin()(this should be visible). - Prompt the user for a password using the
rpasswordcrate (this must be hidden). - Compare the input against a hardcoded set of credentials (e.g., Username: "admin", Password: "rust_is_safe").
- If the credentials match, print "Access Granted"; otherwise, print "Access Denied".
- Ensure that the password variable is dropped as soon as the comparison is finished.
There are no comments for now.