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
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.
When you create one, you want security + usability. You should:
- 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)
Install required Python packages:
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.
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()))
- 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()
Storing Data with SQLite
With SQLite, store:
- 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()
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()
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()
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.
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
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()
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
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!