Password management is one of the most critical activities for being secure online. But with several dozen (if not hundreds) of accounts, how do you remember difficult, new passwords for every website? That's where a password manager is useful. Though there are plenty of excellent password managers available (such as Bitwarden, LastPass, and 1Password), creating your own is a fun and instructive cybersecurity project. In this blog post, we'll show you how to create a basic, secure password manager from scratch. Whether you're a cybersecurity student, enthusiast, or just curious about how things work, this tutorial will explain the key concepts at play. Prefer watching instead of reading? Here’s a quick video guide Prefer watching instead of reading? Here’s a quick video guide Prefer watching instead of reading? Here’s a quick video guide Prefer watching instead of reading? Here’s a quick video guide https://youtu.be/Q1si8Ro0HxY?embedable=true https://youtu.be/Q1si8Ro0HxY?embedable=true What Is a Password Manager? A password manager is a utility that: Saves login info (username + password). Encrypts the data so that only you can see it. Allows you to read passwords securely using a master password. Saves login info (username + password). Encrypts the data so that only you can see it. Allows you to read passwords securely using a master password. When you create one, you want security + usability. You should: Store data securely. Use encryption. Block unauthorized access. Make it simple to use. Store data securely. Use encryption. Block unauthorized access. Make it simple to use. Select Your Tech Stack For this project, we're going to keep things basic and use: Python (programming language) SQLite (for database) Cryptography library (encryption use) Python (programming language) SQLite (for database) Cryptography library (encryption use) Install required Python packages: pip install cryptography pip install cryptography Learn Encryption Basics Encryption is the most critical component of a password manager. We will employ Fernet symmetric encryption from the cryptography library. Here's how it works: A master password is used to create a key. That key is utilized for password entry encryption and decryption. A master password is used to create a key. That key is utilized for password entry encryption and decryption. We’ll derive the encryption key from your master password using a Key Derivation Function (KDF) — specifically PBKDF2HMAC. Setting Up the Master Password When the user opens the app, ask for the master password. Use it to derive the encryption key. Here’s how to derive the key: from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC from cryptography.hazmat.primitives import hashes from cryptography.hazmat.backends import default_backend import base64 import os def get_key_from_password(password, salt): kdf = PBKDF2HMAC( algorithm=hashes.SHA256(), length=32, salt=salt, iterations=100_000, backend=default_backend() ) return base64.urlsafe_b64encode(kdf.derive(password.encode())) from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC from cryptography.hazmat.primitives import hashes from cryptography.hazmat.backends import default_backend import base64 import os def get_key_from_password(password, salt): kdf = PBKDF2HMAC( algorithm=hashes.SHA256(), length=32, salt=salt, iterations=100_000, backend=default_backend() ) return base64.urlsafe_b64encode(kdf.derive(password.encode())) Salt is a random value to make password cracking harder. You need to store the salt (in a file or database) and reuse it each time you derive the key. Salt is a random value to make password cracking harder. You need to store the salt (in a file or database) and reuse it each time you derive the key. Encrypt and Decrypt Passwords With the derived key, encrypt and decrypt the stored passwords: from cryptography.fernet import Fernet def encrypt_password(key, password): fernet = Fernet(key) return fernet.encrypt(password.encode()) def decrypt_password(key, token): fernet = Fernet(key) return fernet.decrypt(token).decode() from cryptography.fernet import Fernet def encrypt_password(key, password): fernet = Fernet(key) return fernet.encrypt(password.encode()) def decrypt_password(key, token): fernet = Fernet(key) return fernet.decrypt(token).decode() Storing Data with SQLite With SQLite, store: Website name Username Encrypted password Website name Username Encrypted password Create the database and table: import sqlite3 def create_db(): conn = sqlite3.connect('vault.db') c = conn.cursor() c.execute(''' CREATE TABLE IF NOT EXISTS passwords ( id INTEGER PRIMARY KEY, website TEXT NOT NULL, username TEXT NOT NULL, password BLOB NOT NULL ) ''') conn.commit() conn.close() import sqlite3 def create_db(): conn = sqlite3.connect('vault.db') c = conn.cursor() c.execute(''' CREATE TABLE IF NOT EXISTS passwords ( id INTEGER PRIMARY KEY, website TEXT NOT NULL, username TEXT NOT NULL, password BLOB NOT NULL ) ''') conn.commit() conn.close() Saving and Retrieving Passwords Here's how to save an entry: def save_password(website, username, password, key): encrypted = encrypt_password(key, password) conn = sqlite3.connect('vault.db') c = conn.cursor() c.execute('INSERT INTO passwords (website, username, password) VALUES (?, ?, ?)', (website, username, encrypted)) conn.commit() conn.close() def save_password(website, username, password, key): encrypted = encrypt_password(key, password) conn = sqlite3.connect('vault.db') c = conn.cursor() c.execute('INSERT INTO passwords (website, username, password) VALUES (?, ?, ?)', (website, username, encrypted)) conn.commit() conn.close() To retrieve and decrypt: def get_passwords(key): conn = sqlite3.connect('vault.db') c = conn.cursor() c.execute('SELECT website, username, password FROM passwords') for row in c.fetchall(): website, username, encrypted_pw = row decrypted_pw = decrypt_password(key, encrypted_pw) print(f'Website: {website} | Username: {username} | Password: {decrypted_pw}') conn.close() def get_passwords(key): conn = sqlite3.connect('vault.db') c = conn.cursor() c.execute('SELECT website, username, password FROM passwords') for row in c.fetchall(): website, username, encrypted_pw = row decrypted_pw = decrypt_password(key, encrypted_pw) print(f'Website: {website} | Username: {username} | Password: {decrypted_pw}') conn.close() Protect Your Master Key and Salt When setting the master password for the first time: Generate random salt and store in a file (salt.bin). On each login, load the salt and derive the key. Generate random salt and store in a file (salt.bin). On each login, load the salt and derive the key. def load_or_create_salt(): if not os.path.exists('salt.bin'): salt = os.urandom(16) with open('salt.bin', 'wb') as f: f.write(salt) else: with open('salt.bin', 'rb') as f: salt = f.read() return salt def load_or_create_salt(): if not os.path.exists('salt.bin'): salt = os.urandom(16) with open('salt.bin', 'wb') as f: f.write(salt) else: with open('salt.bin', 'rb') as f: salt = f.read() return salt Final Program Flow Prompt for master password. Load salt, derive key. Prompt the user: Save new password Get all passwords Prompt for master password. Load salt, derive key. Prompt the user: Save new password Get all passwords Save new password Get all passwords Save new password Get all passwords A simple interface could be as follows: def main(): salt = load_or_create_salt() master_pw = input("Enter your master password: ") key = get_key_from_password(master_pw, salt) while True: choice = input("1. Save Password\n2. View Passwords\n3. Exit\nChoose: ") if choice == '1': site = input("Website: ") user = input("Username: ") pw = input("Password: ") save_password(site, user, pw, key) elif choice == '2': get_passwords(key) else: break if __name__ == "__main__": create_db() main() def main(): salt = load_or_create_salt() master_pw = input("Enter your master password: ") key = get_key_from_password(master_pw, salt) while True: choice = input("1. Save Password\n2. View Passwords\n3. Exit\nChoose: ") if choice == '1': site = input("Website: ") user = input("Username: ") pw = input("Password: ") save_password(site, user, pw, key) elif choice == '2': get_passwords(key) else: break if __name__ == "__main__": create_db() main() Security Best Practices To make your password manager really secure: Do not hardcode passwords or keys. Always encrypt sensitive information. Securely lock the manager after a certain time of inactivity. You may also add: Pass strength checker UI using Tkinter or Flask Export/Import option Backup and restore Do not hardcode passwords or keys. Always encrypt sensitive information. Securely lock the manager after a certain time of inactivity. You may also add: Pass strength checker UI using Tkinter or Flask Export/Import option Backup and restore Pass strength checker UI using Tkinter or Flask Export/Import option Backup and restore Pass strength checker UI using Tkinter or Flask Export/Import option Backup and restore Final Thoughts Building a safe password manager isn't only a fun project — it's an active learning experience for encryption, secure storage, and security best practices. You can use it yourself or simply build it for learning purposes, either way, the project will take your knowledge of security basics to the next level. If you'd prefer the entire source code as a GitHub repo or would like to take this on to the web version, give me a shout. I'd be happy to help take it further!