# Python - SECURE: Strong password security implementation
import bcrypt
import secrets
import sqlite3
import time
import re
import hashlib
import hmac
from datetime import datetime, timedelta
from typing import Tuple, Optional, List
import logging
class SecureAuthSystem:
def __init__(self, db_path="secure_users.db"):
self.conn = sqlite3.connect(db_path, check_same_thread=False)
self.create_tables()
self.bcrypt_rounds = 14 # Secure work factor
self.max_login_attempts = 5
self.lockout_duration = timedelta(minutes=15)
self.password_history_count = 5
# Password policy
self.min_password_length = 12
self.max_password_length = 128
# Common passwords (in production, load from file)
self.common_passwords = self._load_common_passwords()
# Setup logging
logging.basicConfig(level=logging.INFO)
self.logger = logging.getLogger(__name__)
def create_tables(self):
cursor = self.conn.cursor()
# Users table with security fields
cursor.execute("""
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
username TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
password_created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
failed_login_attempts INTEGER DEFAULT 0,
locked_until TIMESTAMP NULL,
requires_password_change BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
# Password history table
cursor.execute("""
CREATE TABLE IF NOT EXISTS password_history (
id INTEGER PRIMARY KEY,
user_id INTEGER,
password_hash TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users (id)
)
""")
# Login attempts table for monitoring
cursor.execute("""
CREATE TABLE IF NOT EXISTS login_attempts (
id INTEGER PRIMARY KEY,
username TEXT,
ip_address TEXT,
success BOOLEAN,
attempted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
self.conn.commit()
def _load_common_passwords(self) -> set:
"""Load common passwords list"""
common = {
'password', '123456', 'password123', 'admin', 'qwerty',
'letmein', 'welcome', 'monkey', '1234567890', 'abc123',
'12345678', 'princess', 'dragon', 'sunshine', 'iloveyou'
}
return common
def validate_password_strength(self, password: str, username: str = None) -> Tuple[bool, str]:
"""Comprehensive password strength validation"""
if len(password) < self.min_password_length:
return False, f"Password must be at least {self.min_password_length} characters long"
if len(password) > self.max_password_length:
return False, f"Password must be no more than {self.max_password_length} characters long"
# Check character requirements
if not re.search(r'[A-Z]', password):
return False, "Password must contain at least one uppercase letter"
if not re.search(r'[a-z]', password):
return False, "Password must contain at least one lowercase letter"
if not re.search(r'\d', password):
return False, "Password must contain at least one number"
if not re.search(r'[!@#$%^&*()_+\-=\[\]{}|;:,.<>?]', password):
return False, "Password must contain at least one special character"
# Check for username in password
if username and username.lower() in password.lower():
return False, "Password cannot contain username"
# Check against common passwords
if password.lower() in self.common_passwords:
return False, "Password is too common"
# Check for repeated characters
if self._has_repeated_patterns(password):
return False, "Password contains too many repeated patterns"
# Check for sequential characters
if self._has_sequential_patterns(password):
return False, "Password contains sequential patterns"
return True, "Password meets security requirements"
def _has_repeated_patterns(self, password: str) -> bool:
"""Check for repeated character patterns"""
# Check for 3+ consecutive identical characters
for i in range(len(password) - 2):
if password[i] == password[i+1] == password[i+2]:
return True
return False
def _has_sequential_patterns(self, password: str) -> bool:
"""Check for sequential character patterns"""
sequences = ['abc', '123', 'qwe', 'asd', 'zxc']
password_lower = password.lower()
for seq in sequences:
if seq in password_lower or seq[::-1] in password_lower:
return True
return False
def hash_password(self, password: str) -> str:
"""Securely hash password using bcrypt"""
password_bytes = password.encode('utf-8')
salt = bcrypt.gensalt(rounds=self.bcrypt_rounds)
hashed = bcrypt.hashpw(password_bytes, salt)
return hashed.decode('utf-8')
def verify_password(self, password: str, hashed_password: str) -> bool:
"""Verify password against hash using timing-safe comparison"""
try:
password_bytes = password.encode('utf-8')
hashed_bytes = hashed_password.encode('utf-8')
return bcrypt.checkpw(password_bytes, hashed_bytes)
except (ValueError, TypeError):
return False
def is_account_locked(self, username: str) -> bool:
"""Check if account is locked"""
cursor = self.conn.cursor()
cursor.execute(
"SELECT locked_until FROM users WHERE username = ?",
(username,)
)
result = cursor.fetchone()
if not result or not result[0]:
return False
locked_until = datetime.fromisoformat(result[0])
return datetime.now() < locked_until
def record_login_attempt(self, username: str, success: bool, ip_address: str = None):
"""Record login attempt for monitoring"""
cursor = self.conn.cursor()
# Record in login attempts table
cursor.execute(
"INSERT INTO login_attempts (username, ip_address, success) VALUES (?, ?, ?)",
(username, ip_address, success)
)
if not success:
# Increment failed attempts
cursor.execute(
"UPDATE users SET failed_login_attempts = failed_login_attempts + 1 WHERE username = ?",
(username,)
)
# Check if we should lock the account
cursor.execute(
"SELECT failed_login_attempts FROM users WHERE username = ?",
(username,)
)
result = cursor.fetchone()
if result and result[0] >= self.max_login_attempts:
locked_until = datetime.now() + self.lockout_duration
cursor.execute(
"UPDATE users SET locked_until = ? WHERE username = ?",
(locked_until.isoformat(), username)
)
self.logger.warning(f"Account {username} locked due to {result[0]} failed attempts")
else:
# Reset failed attempts on successful login
cursor.execute(
"UPDATE users SET failed_login_attempts = 0, locked_until = NULL WHERE username = ?",
(username,)
)
self.conn.commit()
def check_password_history(self, user_id: int, new_password: str) -> bool:
"""Check if password was used recently"""
cursor = self.conn.cursor()
cursor.execute(
"""SELECT password_hash FROM password_history
WHERE user_id = ?
ORDER BY created_at DESC
LIMIT ?""",
(user_id, self.password_history_count)
)
for (old_hash,) in cursor.fetchall():
if self.verify_password(new_password, old_hash):
return False # Password was used before
return True # Password is new
def add_to_password_history(self, user_id: int, password_hash: str):
"""Add password to history and clean old entries"""
cursor = self.conn.cursor()
# Add new password to history
cursor.execute(
"INSERT INTO password_history (user_id, password_hash) VALUES (?, ?)",
(user_id, password_hash)
)
# Clean old password history entries
cursor.execute(
"""DELETE FROM password_history WHERE user_id = ? AND id NOT IN (
SELECT id FROM password_history WHERE user_id = ?
ORDER BY created_at DESC LIMIT ?
)""",
(user_id, user_id, self.password_history_count)
)
self.conn.commit()
def register_user(self, username: str, password: str) -> Tuple[bool, str]:
"""Register new user with comprehensive security checks"""
# Validate username
if not username or len(username) < 3:
return False, "Username must be at least 3 characters long"
if not re.match(r'^[a-zA-Z0-9_]+$', username):
return False, "Username can only contain letters, numbers, and underscores"
# Validate password strength
is_valid, message = self.validate_password_strength(password, username)
if not is_valid:
return False, message
# Hash password
password_hash = self.hash_password(password)
cursor = self.conn.cursor()
try:
cursor.execute(
"INSERT INTO users (username, password_hash) VALUES (?, ?)",
(username, password_hash)
)
user_id = cursor.lastrowid
# Add to password history
self.add_to_password_history(user_id, password_hash)
self.conn.commit()
self.logger.info(f"User {username} registered successfully")
return True, "User registered successfully"
except sqlite3.IntegrityError:
return False, "Username already exists"
def login_user(self, username: str, password: str, ip_address: str = None) -> Tuple[bool, str]:
"""Authenticate user with comprehensive security checks"""
# Check if account is locked
if self.is_account_locked(username):
self.record_login_attempt(username, False, ip_address)
return False, "Account is temporarily locked due to too many failed attempts"
cursor = self.conn.cursor()
cursor.execute(
"SELECT id, password_hash, requires_password_change FROM users WHERE username = ?",
(username,)
)
result = cursor.fetchone()
if not result:
self.record_login_attempt(username, False, ip_address)
return False, "Invalid username or password"
user_id, stored_hash, requires_change = result
# Verify password
if not self.verify_password(password, stored_hash):
self.record_login_attempt(username, False, ip_address)
return False, "Invalid username or password"
# Successful login
self.record_login_attempt(username, True, ip_address)
if requires_change:
return True, "Login successful - password change required"
self.logger.info(f"User {username} logged in successfully")
return True, "Login successful"
def change_password(self, username: str, old_password: str, new_password: str) -> Tuple[bool, str]:
"""Change user password with security validation"""
# Verify current password
login_success, _ = self.login_user(username, old_password)
if not login_success:
return False, "Current password is incorrect"
# Get user ID
cursor = self.conn.cursor()
cursor.execute("SELECT id FROM users WHERE username = ?", (username,))
result = cursor.fetchone()
if not result:
return False, "User not found"
user_id = result[0]
# Validate new password strength
is_valid, message = self.validate_password_strength(new_password, username)
if not is_valid:
return False, message
# Check password history
if not self.check_password_history(user_id, new_password):
return False, f"Password cannot be one of your last {self.password_history_count} passwords"
# Hash new password
new_password_hash = self.hash_password(new_password)
# Update password
cursor.execute(
"""UPDATE users SET
password_hash = ?,
password_created_at = CURRENT_TIMESTAMP,
requires_password_change = FALSE,
updated_at = CURRENT_TIMESTAMP
WHERE username = ?""",
(new_password_hash, username)
)
# Add to password history
self.add_to_password_history(user_id, new_password_hash)
self.conn.commit()
self.logger.info(f"Password changed successfully for user {username}")
return True, "Password changed successfully"
def generate_secure_password(self, length: int = 16) -> str:
"""Generate cryptographically secure password"""
if length < self.min_password_length:
length = self.min_password_length
import string
# Character sets
lowercase = string.ascii_lowercase
uppercase = string.ascii_uppercase
digits = string.digits
special = '!@#$%^&*()_+-=[]{}|;:,.<>?'
# Ensure at least one character from each set
password = [
secrets.choice(lowercase),
secrets.choice(uppercase),
secrets.choice(digits),
secrets.choice(special)
]
# Fill the rest with random characters
all_chars = lowercase + uppercase + digits + special
for _ in range(length - 4):
password.append(secrets.choice(all_chars))
# Shuffle the password
secrets.SystemRandom().shuffle(password)
return ''.join(password)
def get_security_metrics(self) -> dict:
"""Get security metrics for monitoring"""
cursor = self.conn.cursor()
# Recent failed login attempts
cursor.execute(
"""SELECT COUNT(*) FROM login_attempts
WHERE success = FALSE AND attempted_at > datetime('now', '-1 hour')"""
)
recent_failures = cursor.fetchone()[0]
# Locked accounts
cursor.execute(
"SELECT COUNT(*) FROM users WHERE locked_until > datetime('now')"
)
locked_accounts = cursor.fetchone()[0]
# Users requiring password change
cursor.execute(
"SELECT COUNT(*) FROM users WHERE requires_password_change = TRUE"
)
require_change = cursor.fetchone()[0]
return {
'recent_failed_attempts': recent_failures,
'locked_accounts': locked_accounts,
'users_requiring_password_change': require_change
}
# Example usage
if __name__ == "__main__":
auth_system = SecureAuthSystem()
# Register a user
success, message = auth_system.register_user("testuser", "MySecureP@ssw0rd123!")
print(f"Registration: {success} - {message}")
# Login
success, message = auth_system.login_user("testuser", "MySecureP@ssw0rd123!", "192.168.1.1")
print(f"Login: {success} - {message}")
# Generate secure password
secure_pwd = auth_system.generate_secure_password(20)
print(f"Generated secure password: {secure_pwd}")
# Get security metrics
metrics = auth_system.get_security_metrics()
print(f"Security metrics: {metrics}")