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 password
  • secure_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.

Leave a Reply

Your email address will not be published. Required fields are marked *