From 167fa2a5954dfd8eb5075cb4dd509df941160202 Mon Sep 17 00:00:00 2001 From: Branimir Georgiev <66906831+braboj@users.noreply.github.com> Date: Fri, 20 Jun 2025 08:33:01 +0300 Subject: [PATCH] Add Secure Design docs entry --- .../B04_secure_design/input_validation.py | 19 ++++++++++++++++++ .../B04_secure_design/password_storage_bad.py | 12 +++++++++++ .../password_storage_good.py | 20 +++++++++++++++++++ examples/B04_secure_design/secret_from_env.py | 12 +++++++++++ .../B04_secure_design/sql_injection_bad.py | 18 +++++++++++++++++ .../B04_secure_design/sql_injection_good.py | 18 +++++++++++++++++ mkdocs.yml | 1 + 7 files changed, 100 insertions(+) create mode 100644 examples/B04_secure_design/input_validation.py create mode 100644 examples/B04_secure_design/password_storage_bad.py create mode 100644 examples/B04_secure_design/password_storage_good.py create mode 100644 examples/B04_secure_design/secret_from_env.py create mode 100644 examples/B04_secure_design/sql_injection_bad.py create mode 100644 examples/B04_secure_design/sql_injection_good.py diff --git a/examples/B04_secure_design/input_validation.py b/examples/B04_secure_design/input_validation.py new file mode 100644 index 0000000..be01aaf --- /dev/null +++ b/examples/B04_secure_design/input_validation.py @@ -0,0 +1,19 @@ +# Example: Input Validation +# ------------------------------------------------------------------------------ +# Validates user input before using it. This prevents unexpected behaviour +# or vulnerabilities such as injection or crashes. + +import re + +user_input = input('Enter an age (0-120): ') + +# Accept only digits using a regular expression +if not re.fullmatch(r"\d+", user_input): + raise ValueError('Input must be a positive integer') + +age = int(user_input) + +if not 0 <= age <= 120: + raise ValueError('Age out of expected range') + +print(f'Your age is {age}') diff --git a/examples/B04_secure_design/password_storage_bad.py b/examples/B04_secure_design/password_storage_bad.py new file mode 100644 index 0000000..fa4e338 --- /dev/null +++ b/examples/B04_secure_design/password_storage_bad.py @@ -0,0 +1,12 @@ +# Password Storage - Bad Example +# ------------------------------------------------------------------------------ +# Storing or hashing passwords without a salt and a strong hash function makes +# them easy to crack if the database is compromised. + +import hashlib + +password = input('Enter password: ') + +# BAD: Unsalted, single round MD5 hash +hash_value = hashlib.md5(password.encode('utf-8')).hexdigest() +print('Storing password hash:', hash_value) diff --git a/examples/B04_secure_design/password_storage_good.py b/examples/B04_secure_design/password_storage_good.py new file mode 100644 index 0000000..4e3e5c2 --- /dev/null +++ b/examples/B04_secure_design/password_storage_good.py @@ -0,0 +1,20 @@ +# Password Storage - Good Example +# ------------------------------------------------------------------------------ +# Use a strong key derivation function with a unique salt for each password. +# PBKDF2 is built into Python's hashlib module. + +import os +import hashlib +import base64 + +password = input('Enter password: ') + +salt = os.urandom(16) +hash_bytes = hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'), salt, 100_000) + +encoded_salt = base64.b64encode(salt).decode('utf-8') +encoded_hash = base64.b64encode(hash_bytes).decode('utf-8') + +print('Storing password hash:') +print(f'salt={encoded_salt}') +print(f'hash={encoded_hash}') diff --git a/examples/B04_secure_design/secret_from_env.py b/examples/B04_secure_design/secret_from_env.py new file mode 100644 index 0000000..868fc19 --- /dev/null +++ b/examples/B04_secure_design/secret_from_env.py @@ -0,0 +1,12 @@ +# Secret Management +# ------------------------------------------------------------------------------ +# Retrieve secrets like API keys from environment variables instead of +# hardcoding them in the source code. + +import os + +api_key = os.getenv('API_KEY') +if api_key is None: + raise RuntimeError('API_KEY environment variable is not set') + +print('Loaded API key of length', len(api_key)) diff --git a/examples/B04_secure_design/sql_injection_bad.py b/examples/B04_secure_design/sql_injection_bad.py new file mode 100644 index 0000000..daf3c39 --- /dev/null +++ b/examples/B04_secure_design/sql_injection_bad.py @@ -0,0 +1,18 @@ +# SQL Injection - Bad Example +# ------------------------------------------------------------------------------ +# Directly formatting user input into an SQL query can allow injection attacks. +# NEVER build queries using string concatenation with untrusted data. + +import sqlite3 + +conn = sqlite3.connect(':memory:') +cursor = conn.cursor() +cursor.execute('CREATE TABLE users(id INTEGER PRIMARY KEY, username TEXT)') + +username = input('Enter username to lookup: ') + +# BAD: user input is concatenated directly into the query +query = f"SELECT * FROM users WHERE username = '{username}'" +print('Executing:', query) +for row in cursor.execute(query): + print(row) diff --git a/examples/B04_secure_design/sql_injection_good.py b/examples/B04_secure_design/sql_injection_good.py new file mode 100644 index 0000000..d7e2a2b --- /dev/null +++ b/examples/B04_secure_design/sql_injection_good.py @@ -0,0 +1,18 @@ +# SQL Injection - Good Example +# ------------------------------------------------------------------------------ +# Parameterized queries separate data from code, preventing SQL injection. +# Use parameter placeholders and pass parameters as a tuple. + +import sqlite3 + +conn = sqlite3.connect(':memory:') +cursor = conn.cursor() +cursor.execute('CREATE TABLE users(id INTEGER PRIMARY KEY, username TEXT)') + +username = input('Enter username to lookup: ') + +# GOOD: query uses ? placeholders and parameters tuple +query = 'SELECT * FROM users WHERE username = ?' +print('Executing:', query, 'with params', username) +for row in cursor.execute(query, (username,)): + print(row) diff --git a/mkdocs.yml b/mkdocs.yml index a2a6494..ed7de8b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -28,6 +28,7 @@ nav: - OOP Basics: B01_oop_pillars.md - SOLID Principles: B02_solid_principles.md - Design Patterns: B03_design_patterns.md + - Secure Design: B04_secure_design.md - Libraries: - Logging: C01_logging.md