Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions examples/B04_secure_design/input_validation.py
Original file line number Diff line number Diff line change
@@ -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}')
12 changes: 12 additions & 0 deletions examples/B04_secure_design/password_storage_bad.py
Original file line number Diff line number Diff line change
@@ -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)
20 changes: 20 additions & 0 deletions examples/B04_secure_design/password_storage_good.py
Original file line number Diff line number Diff line change
@@ -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}')
12 changes: 12 additions & 0 deletions examples/B04_secure_design/secret_from_env.py
Original file line number Diff line number Diff line change
@@ -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))
18 changes: 18 additions & 0 deletions examples/B04_secure_design/sql_injection_bad.py
Original file line number Diff line number Diff line change
@@ -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)
18 changes: 18 additions & 0 deletions examples/B04_secure_design/sql_injection_good.py
Original file line number Diff line number Diff line change
@@ -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)
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down