Skip to content
Threats Guide

parameterized_queries.py

SQL Injection Prevention - Parameterized Queries

  • prevention
  • python
  • 373 lines

Run security tools only on systems you own or have written permission to test.

Run it

From the repository root, after pip install -r tools/requirements.txt
cd 02-web-application-security/sql-injection/prevention
python3 parameterized_queries.py --help

Source

View on GitHub
#!/usr/bin/env python3"""SQL Injection Prevention - Parameterized QueriesLocation: 02-web-application-security/sql-injection/prevention/parameterized_queries.py""" import sqlite3import mysql.connectorfrom mysql.connector import Errorimport psycopg2from psycopg2 import sqlimport hashlibimport os class SecureDatabaseQueries:    """    Example class demonstrating secure database queries using parameterization    to prevent SQL injection attacks.    """        def __init__(self, db_type='sqlite', db_name='secure_app.db'):        self.db_type = db_type        self.db_name = db_name        self.connection = None     @staticmethod    def hash_password(password):        """Salted scrypt hash. Plain SHA-256 is too fast to protect passwords."""        salt = os.urandom(16)        digest = hashlib.scrypt(password.encode(), salt=salt, n=2**14, r=8, p=1)        return f"scrypt${salt.hex()}${digest.hex()}"     def _q(self, query):        """Translate %s placeholders to ? for SQLite, which does not accept %s."""        return query.replace('%s', '?') if self.db_type == 'sqlite' else query     def connect(self):        """Establish database connection based on type"""        try:            if self.db_type == 'sqlite':                self.connection = sqlite3.connect(self.db_name)                print("[✓] Connected to SQLite database")                            elif self.db_type == 'mysql':                # Example MySQL connection (update with your credentials)                self.connection = mysql.connector.connect(                    host='localhost',                    database=self.db_name,                    user='your_user',                    password='your_password'                )                print("[✓] Connected to MySQL database")                            elif self.db_type == 'postgresql':                # Example PostgreSQL connection                self.connection = psycopg2.connect(                    host='localhost',                    database=self.db_name,                    user='your_user',                    password='your_password'                )                print("[✓] Connected to PostgreSQL database")                        except Exception as e:            print(f"[!] Database connection error: {e}")            return False        return True        def create_users_table(self):        """Create example users table"""        try:            cursor = self.connection.cursor()                        if self.db_type == 'sqlite':                cursor.execute('''                    CREATE TABLE IF NOT EXISTS users (                        id INTEGER PRIMARY KEY AUTOINCREMENT,                        username TEXT UNIQUE NOT NULL,                        email TEXT UNIQUE NOT NULL,                        password_hash TEXT NOT NULL,                        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP                    )                ''')            elif self.db_type == 'mysql':                cursor.execute('''                    CREATE TABLE IF NOT EXISTS users (                        id INT AUTO_INCREMENT PRIMARY KEY,                        username VARCHAR(50) UNIQUE NOT NULL,                        email VARCHAR(100) UNIQUE NOT NULL,                        password_hash VARCHAR(255) NOT NULL,                        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP                    )                ''')            elif self.db_type == 'postgresql':                cursor.execute('''                    CREATE TABLE IF NOT EXISTS users (                        id SERIAL PRIMARY KEY,                        username VARCHAR(50) UNIQUE NOT NULL,                        email VARCHAR(100) UNIQUE NOT NULL,                        password_hash VARCHAR(255) NOT NULL,                        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP                    )                ''')                        self.connection.commit()            print("[✓] Users table created successfully")                    except Exception as e:            print(f"[!] Error creating table: {e}")        # SECURE METHODS - Using parameterized queries        def add_user_secure(self, username, email, password):        """        SECURE: Add a new user using parameterized query        This prevents SQL injection by separating SQL logic from data        """        try:            cursor = self.connection.cursor()                        # Hash the password (never store plain text passwords!)            password_hash = self.hash_password(password)                        # SECURE: Using parameterized query with placeholders            if self.db_type in ['sqlite', 'mysql']:                query = "INSERT INTO users (username, email, password_hash) VALUES (%s, %s, %s)"                cursor.execute(self._q(query), (username, email, password_hash))            elif self.db_type == 'postgresql':                # PostgreSQL uses %s placeholders                query = "INSERT INTO users (username, email, password_hash) VALUES (%s, %s, %s)"                cursor.execute(self._q(query), (username, email, password_hash))                        self.connection.commit()            print(f"[✓] User '{username}' added successfully (ID: {cursor.lastrowid if hasattr(cursor, 'lastrowid') else 'N/A'})")            return True                    except Exception as e:            print(f"[!] Error adding user: {e}")            self.connection.rollback()            return False        def get_user_secure(self, username):        """        SECURE: Retrieve user by username using parameterized query        """        try:            cursor = self.connection.cursor()                        # SECURE: Parameterized query            if self.db_type in ['sqlite', 'mysql', 'postgresql']:                query = "SELECT id, username, email, created_at FROM users WHERE username = %s"                cursor.execute(self._q(query), (username,))                        result = cursor.fetchone()                        if result:                print(f"[✓] User found: {result}")                return result            else:                print(f"[!] User '{username}' not found")                return None                        except Exception as e:            print(f"[!] Error retrieving user: {e}")            return None        def search_users_secure(self, search_term):        """        SECURE: Search users with partial matching using parameterized query        """        try:            cursor = self.connection.cursor()                        # SECURE: Use parameter for the search term with LIKE            # Note: The % wildcards are part of the parameter value, not the SQL            search_pattern = f"%{search_term}%"                        query = "SELECT id, username, email FROM users WHERE username LIKE %s OR email LIKE %s"            cursor.execute(self._q(query), (search_pattern, search_pattern))                        results = cursor.fetchall()                        print(f"[✓] Found {len(results)} user(s) matching '{search_term}':")            for user in results:                print(f"    - ID: {user[0]}, Username: {user[1]}, Email: {user[2]}")                        return results                    except Exception as e:            print(f"[!] Error searching users: {e}")            return []        def update_email_secure(self, user_id, new_email):        """        SECURE: Update user email using parameterized query        """        try:            cursor = self.connection.cursor()                        # SECURE: Parameterized query            query = "UPDATE users SET email = %s WHERE id = %s"            cursor.execute(self._q(query), (new_email, user_id))                        self.connection.commit()                        if cursor.rowcount > 0:                print(f"[✓] Email updated for user ID {user_id}")                return True            else:                print(f"[!] No user found with ID {user_id}")                return False                        except Exception as e:            print(f"[!] Error updating email: {e}")            self.connection.rollback()            return False        def delete_user_secure(self, user_id):        """        SECURE: Delete user by ID using parameterized query        """        try:            cursor = self.connection.cursor()                        # SECURE: Parameterized query            query = "DELETE FROM users WHERE id = %s"            cursor.execute(self._q(query), (user_id,))                        self.connection.commit()                        if cursor.rowcount > 0:                print(f"[✓] User ID {user_id} deleted successfully")                return True            else:                print(f"[!] No user found with ID {user_id}")                return False                        except Exception as e:            print(f"[!] Error deleting user: {e}")            self.connection.rollback()            return False        # INSECURE METHODS - For demonstration only (DO NOT USE)        def add_user_insecure(self, username, email, password):        """        INSECURE: Adding user with string concatenation (VULNERABLE TO SQL INJECTION)        THIS IS FOR DEMONSTRATION ONLY - NEVER USE THIS APPROACH        """        try:            cursor = self.connection.cursor()            password_hash = hashlib.sha256(password.encode()).hexdigest()                        # INSECURE: String concatenation creates SQL injection vulnerability            query = f"INSERT INTO users (username, email, password_hash) VALUES ('{username}', '{email}', '{password_hash}')"                        print(f"[!] INSECURE QUERY: {query}")                        cursor.execute(query)            self.connection.commit()            print(f"[!] User added using INSECURE method")                    except Exception as e:            print(f"[!] Error: {e}")        def demonstrate_injection_attack(self):        """        Demonstrate how SQL injection works (for educational purposes)        """        print("\n" + "="*60)        print("SQL INJECTION DEMONSTRATION")        print("="*60)                # Example of malicious input        malicious_username = "admin' OR '1'='1"        malicious_email = "hacker@example.com"        malicious_password = "anything"                print("\n[!] Malicious input example:")        print(f"    Username: {malicious_username}")                print("\n[🔴] INSECURE METHOD (String Concatenation):")        print("    This would generate a query like:")        print(f"    SELECT * FROM users WHERE username = '{malicious_username}'")        print("    Which becomes:")        print("    SELECT * FROM users WHERE username = 'admin' OR '1'='1'")        print("    This returns ALL users because '1'='1' is always true!")                print("\n[🟢] SECURE METHOD (Parameterized Query):")        print("    The query would be:")        print("    SELECT * FROM users WHERE username = %s")        print("    With parameter: 'admin' OR '1'='1'")        print("    The database treats this as a literal string, not part of the SQL logic")        print("    So it searches for a username that literally contains \"admin' OR '1'='1\"")        print("    Which likely doesn't exist, making the attack fail")                print("\n" + "="*60)        print("KEY TAKEAWAYS:")        print("1. Always use parameterized queries/prepared statements")        print("2. Never concatenate user input directly into SQL strings")        print("3. Validate and sanitize all user input")        print("4. Use least privilege principles for database accounts")        print("="*60)        def close(self):        """Close database connection"""        if self.connection:            self.connection.close()            print("[✓] Database connection closed") def main():    """Example usage of secure database queries"""    print("""    ╔══════════════════════════════════════════════════╗    ║   SQL Injection Prevention - Parameterized Queries  ║    ║                EDUCATIONAL EXAMPLE                  ║    ╚══════════════════════════════════════════════════╝    """)        # Initialize secure database handler    db = SecureDatabaseQueries(db_type='sqlite', db_name='secure_app.db')        if db.connect():        # Create table        db.create_users_table()                # Add users securely        print("\n" + "-"*40)        print("ADDING USERS SECURELY")        print("-"*40)                db.add_user_secure("alice", "alice@example.com", "SecurePass123!")        db.add_user_secure("bob", "bob@example.com", "AnotherPass456!")        db.add_user_secure("charlie", "charlie@example.com", "CharliePass789!")                # Retrieve users        print("\n" + "-"*40)        print("RETRIEVING USERS SECURELY")        print("-"*40)                db.get_user_secure("alice")                # Search users        print("\n" + "-"*40)        print("SEARCHING USERS SECURELY")        print("-"*40)                db.search_users_secure("alice")                # Update user        print("\n" + "-"*40)        print("UPDATING USER SECURELY")        print("-"*40)                db.update_email_secure(1, "alice.new@example.com")                # Demonstrate SQL injection concept        db.demonstrate_injection_attack()                # Close connection        db.close()        print("\n" + "="*60)    print("✅ SECURE CODING PRACTICES IMPLEMENTED:")    print("• Parameterized queries for all database operations")    print("• Password hashing (never stored in plaintext)")    print("• Proper error handling")    print("• Input validation (implicitly through parameterization)")    print("="*60) if __name__ == "__main__":    main()