Practical Python Security

Building a Secure Login System Using Hashing and PostgreSQL

Authentication is one of the most critical parts of any backend system.

A poorly designed login system can expose:

In this article, we build a secure login system using Python, PostgreSQL, hashing, salting, and secure verification.

Why Plain Password Storage is Dangerous

password = "mypassword"

Storing raw passwords means:

Instead, we store hashed passwords.

Step 1 — Database Design (PostgreSQL)

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    username VARCHAR(100) UNIQUE,
    password_hash TEXT NOT NULL,
    salt TEXT NOT NULL
);

Each user has a unique salt to ensure even identical passwords produce different hashes.

Step 2 — Hashing with Salt (Python)

import hashlib
import os

def hash_password(password):
    salt = os.urandom(16)
    hash_value = hashlib.sha256(salt + password.encode()).hexdigest()
    return hash_value, salt.hex()
Same password will generate different hashes due to unique salt.

Step 3 — User Registration

import psycopg2

def register_user(username, password):
    conn = psycopg2.connect(
        dbname="auth",
        user="postgres",
        password="password",
        host="localhost"
    )

    cur = conn.cursor()

    hash_value, salt = hash_password(password)

    cur.execute(
        "INSERT INTO users (username, password_hash, salt) VALUES (%s, %s, %s)",
        (username, hash_value, salt)
    )

    conn.commit()
    conn.close()

Passwords are never stored directly — only hash and salt.

Step 4 — Login Verification

def verify_user(username, password):
    conn = psycopg2.connect(
        dbname="auth",
        user="postgres",
        password="password",
        host="localhost"
    )

    cur = conn.cursor()

    cur.execute(
        "SELECT password_hash, salt FROM users WHERE username = %s",
        (username,)
    )

    result = cur.fetchone()

    if not result:
        return False

    stored_hash, salt = result
    salt_bytes = bytes.fromhex(salt)

    new_hash = hashlib.sha256(salt_bytes + password.encode()).hexdigest()

    conn.close()

    return new_hash == stored_hash

The system compares hashes — not actual passwords.

Step 5 — Token Generation

import secrets

def generate_token():
    return secrets.token_hex(32)

Tokens are used for secure sessions and authentication.

Real Backend Flow

Registration

Login

Why This System is Secure

Real-World Improvements

Key Concepts Learned

Final Thought

A login system is not just a feature — it is a security boundary. Understanding hashing is essential for building secure backend systems.