Sharing notes from my ongoing learning journey — what I build, break and understand along the way.
Building a Secure ATM App in Python with Hashed Passwords and SQLite
Secure Your Python ATM App: From Plain Passwords to SHA-256 Hashing
In my previous post, I created a simple command-line ATM application using Python. It allowed users to check their balance, deposit money, and withdraw funds — all based on a fixed variable (balance
) stored in memory.
This time, I wanted to take the project a step further by adding a real database to store user information — and more importantly, I wanted to make sure passwords were not stored in plain text.
That brought me to a concept called hashing.
If you don’t know what that means, I highly recommend checking out my other post:
👉 What is Hashing? And Why It’s Essential in Login Systems
New Python Concepts I Used
Before jumping into the code, here are the new concepts and tools I learned and used for this version:
sqlite3
This is Python’s built-in module for working with SQLite databases. I used it to store user data (username, password, name, balance) securely.
import sqlite3
hashlib
This module allows me to convert passwords into a secure one-way string using SHA-256.
import hashlib
def hash_password(password):
return hashlib.sha256(password.encode()).hexdigest()
SQL Queries
To interact with the database, I used SELECT
, INSERT
, and UPDATE
commands via Python.
Example:
cursor.execute("SELECT * FROM users WHERE username = ?", (username,))
This helps prevent SQL injection by using parameterized queries.
Database Browser
To inspect the database and verify the hashed passwords, I used DB Browser for SQLite, a free and super helpful tool.
Project Structure
I broke the project into two Python files:
register_user.py
→ lets you register new users with a hashed passwordsecure_atm_app.py
→ runs the ATM app (login + banking operations)
register_user.py
– Secure User Registration
import sqlite3
import hashlib
def hash_password(password):
return hashlib.sha256(password.encode()).hexdigest()
def register():
conn = sqlite3.connect("atm_secure.db")
cursor = conn.cursor()
print("=== REGISTER NEW USER ===")
username = input("Choose a username: ").strip()
name = input("Your name: ").strip()
cursor.execute("SELECT * FROM users WHERE username = ?", (username,))
if cursor.fetchone():
print("❌ This username is already taken.\n")
conn.close()
return
password = input("Choose a password: ").strip()
confirm = input("Confirm password: ").strip()
if password != confirm:
print("❌ Passwords do not match.\n")
conn.close()
return
hashed_pw = hash_password(password)
balance = 0.0
cursor.execute(
"INSERT INTO users (username, name, password, balance) VALUES (?, ?, ?, ?)",
(username, name, hashed_pw, balance)
)
conn.commit()
conn.close()
print(f"✅ User '{username}' registered successfully with balance: ${balance:.2f}.\n")
if __name__ == "__main__":
register()
secure_atm_app.py
– Login and Use the ATM
import sqlite3
import hashlib
def hash_password(password):
return hashlib.sha256(password.encode()).hexdigest()
def login():
conn = sqlite3.connect("atm_secure.db")
cursor = conn.cursor()
print("=== LOGIN ===")
while True:
username = input("Username: ").strip()
password = input("Password: ").strip()
hashed_pw = hash_password(password)
cursor.execute("SELECT * FROM users WHERE username = ? AND password = ?", (username, hashed_pw))
user = cursor.fetchone()
if user:
print(f"\n✅ Welcome, {user[2]}!")
conn.close()
return user
else:
print("❌ Incorrect username or password. Please try again.\n")
def atm_menu(user_id, user_name):
while True:
print(f"""\n=== ATM MENU (User: {user_name}) ===
1. Check Balance
2. Deposit Money
3. Withdraw Money
q. Quit
""")
choice = input("Select an option: ").strip()
conn = sqlite3.connect("atm_secure.db")
cursor = conn.cursor()
if choice == "1":
cursor.execute("SELECT balance FROM users WHERE id = ?", (user_id,))
balance = cursor.fetchone()[0]
print(f"💰 Your balance: {balance:.2f} USD")
elif choice == "2":
try:
amount = float(input("Enter amount to deposit: "))
if amount <= 0:
print("⚠️ Amount must be positive.")
continue
cursor.execute("UPDATE users SET balance = balance + ? WHERE id = ?", (amount, user_id))
conn.commit()
print(f"✅ Deposited {amount:.2f} USD.")
except ValueError:
print("❌ Invalid input. Please enter a number.")
elif choice == "3":
try:
amount = float(input("Enter amount to withdraw: "))
cursor.execute("SELECT balance FROM users WHERE id = ?", (user_id,))
balance = cursor.fetchone()[0]
if amount <= 0:
print("⚠️ Amount must be positive.")
elif amount > balance:
print("❌ Insufficient funds.")
else:
cursor.execute("UPDATE users SET balance = balance - ? WHERE id = ?", (amount, user_id))
conn.commit()
print(f"✅ Withdrawn {amount:.2f} USD.")
except ValueError:
print("❌ Invalid input. Please enter a number.")
elif choice.lower() == "q":
print("👋 Goodbye!")
conn.close()
break
else:
print("❌ Invalid selection. Try again.")
conn.close()
def main():
user = login()
user_id = user[0]
user_name = user[2]
atm_menu(user_id, user_name)
if __name__ == "__main__":
main()
What the Database Looks Like
After registering a few test users, I opened the database using DB Browser for SQLite. Here’s how it looks:

Note how the password
column stores SHA-256 hashes instead of the real passwords. This makes the app safer, even if someone gets access to the .db
file.
In Short
This project was a major step forward for me. I moved from using variables to using a real database, learned about hashing, and built a secure user system — all with Python.