Chapter 27 Exercises: Security-First Development

Tier 1: Remember & Understand (Exercises 1–6)

Exercise 1: Security Vocabulary Matching

Match each security term with its correct definition.

Term Definition
A. SQL Injection 1. Converting special characters to HTML entities before rendering
B. XSS 2. A browser header that restricts resource loading
C. Output encoding 3. Inserting malicious SQL through user-supplied input
D. CSP 4. Injecting malicious scripts into web pages viewed by other users
E. bcrypt 5. Accepting only inputs matching known-good patterns
F. Whitelist validation 6. A slow, salted hash function designed for password storage

Exercise 2: True or False — Security Fundamentals

Determine whether each statement is true or false. Explain your reasoning.

  1. AI-generated code is inherently more secure than human-written code because AI learns from best practices.
  2. Blacklist validation is preferred over whitelist validation because it catches more attacks.
  3. Parameterized queries prevent SQL injection by separating SQL code from data.
  4. Storing JWTs in localStorage is a recommended practice because it persists across browser sessions.
  5. The HttpOnly flag on a cookie prevents JavaScript from reading the cookie's value.
  6. Using an ORM guarantees that your application is safe from SQL injection.

Exercise 3: Identify the Vulnerability

For each code snippet, identify the security vulnerability and name the attack it enables.

Snippet A:

def search_users(query):
    sql = f"SELECT * FROM users WHERE name LIKE '%{query}%'"
    return db.execute(sql)

Snippet B:

@app.route('/profile')
def profile():
    name = request.args.get('name', '')
    return f"<h1>Welcome, {name}</h1>"

Snippet C:

API_KEY = "sk-proj-abc123def456ghi789jkl012"
client = openai.OpenAI(api_key=API_KEY)

Snippet D:

import hashlib
def hash_password(password):
    return hashlib.md5(password.encode()).hexdigest()

Exercise 4: Security Principles Identification

For each scenario, identify which of the three security pillars applies: (a) Never trust, always verify, (b) Least privilege, (c) Defense in depth.

  1. A database user account has only SELECT and INSERT permissions, not DELETE or DROP.
  2. The application validates input at the API layer and again at the database layer.
  3. A function checks the JWT signature, verifies the expiration, and confirms the issuer claim.
  4. A microservice communicates only on the ports it needs and has no access to other services' databases.
  5. Both server-side validation and client-side validation reject invalid email formats.

Exercise 5: OWASP Top 10 Classification

Classify each of the following vulnerabilities according to the OWASP Top 10 category it belongs to:

  1. A user changes the URL from /api/orders/42 to /api/orders/43 and sees another user's order.
  2. The application uses pickle.loads() on user-supplied data.
  3. An error page displays a full stack trace including database connection strings.
  4. The application does not enforce rate limiting on its password reset endpoint.
  5. A dependency has a known CVE but has not been updated in 18 months.

Exercise 6: Security Header Analysis

Given the following HTTP response headers, identify what is missing and what risks remain:

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
X-Frame-Options: SAMEORIGIN
Cache-Control: no-cache

List at least five security headers that should be added and explain what each protects against.


Tier 2: Apply (Exercises 7–12)

Exercise 7: Build a Pydantic Validation Schema

Create a Pydantic model called ProductListing that validates: - title: string, 5–200 characters, no HTML tags allowed - description: string, 10–5000 characters - price: float, greater than 0, maximum 999999.99 - category: must be one of: "electronics", "clothing", "books", "home", "sports" - tags: list of strings, maximum 10 tags, each tag 2–30 characters alphanumeric - seller_id: positive integer

Include custom validators for the HTML check on title and the alphanumeric check on tags.

Exercise 8: Parameterize These Queries

Convert the following vulnerable SQL queries to parameterized versions using both raw cursor.execute() and SQLAlchemy text():

# Query 1
cursor.execute(f"SELECT * FROM products WHERE category = '{category}' AND price < {max_price}")

# Query 2
cursor.execute("DELETE FROM sessions WHERE user_id = " + str(user_id))

# Query 3
cursor.execute(f"UPDATE users SET email = '{new_email}' WHERE id = {user_id}")

# Query 4
cursor.execute(f"INSERT INTO logs (action, user_id, details) VALUES ('{action}', {user_id}, '{details}')")

Exercise 9: Implement Password Reset Flow

Implement a secure password reset flow with the following requirements: - Generate a cryptographically secure reset token. - Store the token as a hash (not plaintext) in the database. - The token expires after 30 minutes. - The token is single-use (invalidated after successful reset). - The password reset endpoint validates the new password for complexity. - Rate-limit the reset request endpoint.

Write the request_reset, generate_token, store_token, and verify_and_reset functions.

Exercise 10: Add Security Headers to a Flask App

Create a Flask middleware (using @app.after_request) that adds the following security headers to every response: - Content-Security-Policy with a strict policy - Strict-Transport-Security with a 1-year max-age and includeSubDomains - X-Content-Type-Options: nosniff - X-Frame-Options: DENY - Referrer-Policy: strict-origin-when-cross-origin - Permissions-Policy: disabling camera, microphone, and geolocation

Test your middleware by writing a function that verifies all headers are present.

Exercise 11: Environment-Based Configuration

Create a Config class that: 1. Loads all secrets from environment variables. 2. Validates that all required variables are present at startup (fail fast). 3. Provides type-safe access to configuration values. 4. Has separate configurations for development, testing, and production. 5. Never prints or logs secret values (override __repr__ to mask them).

Required variables: DATABASE_URL, JWT_SECRET, API_KEY, SMTP_PASSWORD, REDIS_URL.

Exercise 12: RBAC Implementation

Implement a complete RBAC system with the following requirements: - Roles: viewer, contributor, editor, moderator, admin - Resources: article, comment, user, settings - Actions: create, read, update, delete - Define a permission matrix. - Implement a check_permission(user_role, resource, action) -> bool function. - Implement a decorator @require_access(resource, action) for Flask routes. - Include tests for at least 10 permission scenarios.


Tier 3: Analyze (Exercises 13–18)

Exercise 13: Security Audit of AI-Generated Code

The following code was generated by an AI assistant. Perform a security audit and identify all vulnerabilities. For each vulnerability, assign a severity (Critical/High/Medium/Low), explain the risk, and provide the corrected code.

from flask import Flask, request, jsonify
import sqlite3
import hashlib
import jwt

app = Flask(__name__)
SECRET = "mysecretkey"
DB_PATH = "app.db"

def get_db():
    return sqlite3.connect(DB_PATH)

@app.route('/register', methods=['POST'])
def register():
    data = request.json
    username = data['username']
    password = data['password']
    hashed = hashlib.sha256(password.encode()).hexdigest()
    db = get_db()
    db.execute(f"INSERT INTO users (username, password) VALUES ('{username}', '{hashed}')")
    db.commit()
    return jsonify({"message": "User created"})

@app.route('/login', methods=['POST'])
def login():
    data = request.json
    username = data['username']
    password = data['password']
    hashed = hashlib.sha256(password.encode()).hexdigest()
    db = get_db()
    user = db.execute(f"SELECT * FROM users WHERE username = '{username}' AND password = '{hashed}'").fetchone()
    if user:
        token = jwt.encode({"user": username}, SECRET, algorithm="HS256")
        return jsonify({"token": token})
    return jsonify({"error": "Invalid credentials"}), 401

@app.route('/profile/<user_id>')
def profile(user_id):
    db = get_db()
    user = db.execute(f"SELECT * FROM users WHERE id = {user_id}").fetchone()
    return jsonify({"username": user[1], "password": user[2]})

@app.route('/search')
def search():
    query = request.args.get('q', '')
    return f"<h1>Results for: {query}</h1>"

if __name__ == '__main__':
    app.run(debug=True)

Exercise 14: JWT Security Analysis

Analyze the following JWT handling code. Identify every security issue and explain the potential impact of each:

import jwt
import time

def create_token(user_data):
    payload = {
        "user": user_data,
        "exp": time.time() + 86400 * 365  # 1 year expiration
    }
    return jwt.encode(payload, "secret", algorithm="HS256")

def verify_token(token):
    try:
        return jwt.decode(token, "secret", algorithms=["HS256", "none"])
    except:
        return None

def get_user_from_token(token):
    data = verify_token(token)
    if data:
        return data["user"]
    return None

Exercise 15: Dependency Risk Assessment

Given the following requirements.txt, analyze the security posture:

flask==2.2.0
requests>=2.20.0
pyyaml==5.3.1
django==3.2.0
sqlalchemy
jinja2==3.0.0
cryptography==3.4.0
paramiko==2.7.0
pillow>=8.0.0
lxml>=4.5.0

For each dependency: 1. Identify potential risks with the version specification. 2. Research whether the specified version has known CVEs. 3. Suggest improvements to the version pinning strategy. 4. Explain why mixing Flask and Django in the same project is a concern.

Exercise 16: Threat Modeling Exercise

You are building a multi-tenant SaaS application for managing medical records. The application uses: - Flask API backend - PostgreSQL database - JWT authentication - AWS S3 for file storage - Stripe for payment processing

Create a threat model by: 1. Drawing a data flow diagram (describe it textually). 2. Identifying all trust boundaries. 3. Listing at least 10 threats using the STRIDE model (Spoofing, Tampering, Repudiation, Information Disclosure, Denial of Service, Elevation of Privilege). 4. For each threat, propose a mitigation strategy. 5. Identify which threats are most critical given that this handles medical data (HIPAA compliance).

Exercise 17: Session Security Comparison

Compare and contrast the security properties of the following session management approaches: 1. Server-side sessions with Redis 2. JWTs stored in HttpOnly cookies 3. JWTs stored in localStorage 4. Signed cookies (no server-side storage)

For each approach, analyze: XSS resistance, CSRF resistance, scalability, revocation capability, and data leakage risk. Create a comparison table and recommend which approach to use for: (a) a banking application, (b) a social media platform, (c) a public API.

Exercise 18: Supply Chain Attack Simulation

A package called python-logging-extra has appeared on PyPI. It claims to provide enhanced logging features. Your AI assistant has suggested adding it to your project. Describe the steps you would take to evaluate this package before installing it. Include at least 8 specific checks and explain why each matters.


Tier 4: Evaluate (Exercises 19–24)

Exercise 19: Security Architecture Review

Review the following architecture decisions and evaluate whether each is secure. For those that are not, propose alternatives:

  1. API keys are passed as query parameters: GET /api/data?key=abc123
  2. User passwords are encrypted (not hashed) so they can be recovered for support purposes.
  3. All microservices share a single database with a single database user account.
  4. Error responses include the full Python traceback in development mode, which is also the mode running in production because "it makes debugging easier."
  5. CORS is set to Access-Control-Allow-Origin: * to support mobile apps.
  6. File uploads are stored in a directory within the web root with their original filenames.

Exercise 20: Evaluate AI-Generated Security Code

An AI assistant generated the following "secure authentication middleware." Evaluate its security and provide a score from 1–10 with justification:

from functools import wraps
from flask import request, jsonify, g
import jwt

def require_auth(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        token = request.headers.get('Authorization', '').replace('Bearer ', '')
        if not token:
            return jsonify({'error': 'No token'}), 401
        try:
            data = jwt.decode(token, 'my-secret-key', algorithms=['HS256'])
            g.user = data
        except jwt.exceptions.DecodeError:
            return jsonify({'error': 'Invalid token'}), 401
        return f(*args, **kwargs)
    return decorated

Exercise 21: Compare Password Hashing Algorithms

Evaluate the following password hashing approaches on the criteria of: security strength, performance characteristics, configuration flexibility, library maturity, and suitability for different use cases.

  1. bcrypt with work factor 12
  2. Argon2id with memory=65536 KB, iterations=3, parallelism=4
  3. scrypt with N=2^14, r=8, p=1
  4. PBKDF2-HMAC-SHA256 with 600,000 iterations

Rank them and justify your ranking. Under what circumstances would you choose each?

Exercise 22: Security Compliance Gap Analysis

Your team has been asked to make your Flask web application OWASP ASVS Level 2 compliant. Research the OWASP Application Security Verification Standard (ASVS) and identify 10 specific requirements from Level 2 that are commonly missed in AI-generated code. For each requirement, explain what it mandates and how to implement it.

Exercise 23: Evaluate Secrets Management Strategies

Compare the following secrets management strategies for a team of 5 developers working on a startup:

  1. .env files with python-dotenv (development + production)
  2. AWS Secrets Manager for production, .env for development
  3. HashiCorp Vault for all environments
  4. Doppler for all environments
  5. Git-crypt for encrypted secrets in the repository

Evaluate each on: cost, complexity, security strength, developer experience, scalability, and disaster recovery. Recommend a strategy for: (a) a bootstrapped startup, (b) a Series A startup with a security-conscious investor, (c) an enterprise.

Exercise 24: Incident Response Planning

A security researcher has responsibly disclosed a vulnerability in your application: the password reset endpoint is vulnerable to account takeover due to predictable tokens. Evaluate the following incident response plan and identify what is missing:

  1. Fix the code to use cryptographically secure random tokens.
  2. Deploy the fix to production.
  3. Send a notification to affected users.

Write a comprehensive incident response plan covering all necessary steps from discovery to post-incident review.


Tier 5: Create (Exercises 25–30)

Exercise 25: Build a Security Middleware Library

Create a Python library called secure_flask that provides Flask middleware for: 1. Automatic security header injection (CSP, HSTS, X-Frame-Options, etc.) 2. Request validation middleware that rejects requests with suspicious payloads 3. Rate limiting per endpoint with configurable limits 4. Automatic CSRF protection for form submissions 5. Request logging that captures security events without logging sensitive data

The library should be configurable through a dictionary and include comprehensive docstrings.

Exercise 26: Create a Secret Scanner

Build a Python tool that scans a directory for hardcoded secrets. The tool must: 1. Detect common secret patterns: API keys, passwords, tokens, connection strings. 2. Use regex patterns for AWS keys, GitHub tokens, Stripe keys, database URLs, and generic high-entropy strings. 3. Respect .gitignore patterns (skip ignored files). 4. Support a .secretsignore file for false positive suppression. 5. Output results in JSON and human-readable formats. 6. Return a non-zero exit code if secrets are found (suitable for CI/CD).

Exercise 27: Build a Comprehensive Security Test Suite

Create a pytest-based security test suite for a user management API with endpoints: POST /register, POST /login, GET /profile/{id}, PUT /profile/{id}, DELETE /profile/{id}. The test suite must include: 1. SQL injection tests for all endpoints 2. XSS tests for all string inputs 3. Authentication bypass tests 4. Authorization tests (IDOR prevention) 5. Rate limiting tests 6. Input validation boundary tests 7. At least 50 individual test cases 8. Clear documentation of what each test verifies

Exercise 28: Design a Secure File Upload System

Design and implement a secure file upload system that: 1. Validates file types by content (magic bytes), not just extension. 2. Enforces file size limits. 3. Sanitizes filenames to prevent path traversal. 4. Stores files outside the web root. 5. Generates unique filenames to prevent overwrites. 6. Scans for malware signatures (basic pattern matching). 7. Strips EXIF data from images to prevent metadata leakage. 8. Serves files through a controlled download endpoint with proper Content-Disposition headers.

Exercise 29: Create an AI Security Prompt Library

Create a collection of 20 security-focused prompts for AI coding assistants. Each prompt should: 1. Target a specific security concern (e.g., "Review this code for SQL injection"). 2. Include specific instructions for what to check. 3. Request structured output (severity, location, fix). 4. Be tested against at least one example vulnerable code snippet.

Organize the prompts into categories: input validation, authentication, authorization, data protection, infrastructure security.

Exercise 30: Build a Security Dashboard

Create a Python application that generates a security dashboard for a project. The dashboard should: 1. Run Bandit and parse its JSON output for code security issues. 2. Run pip-audit and parse results for dependency vulnerabilities. 3. Check for common misconfigurations (debug mode, weak secrets, missing headers). 4. Scan for hardcoded secrets using pattern matching. 5. Generate a summary report with severity counts, top risks, and remediation priority. 6. Output as both JSON and a formatted terminal report with color coding. 7. Assign an overall security score from A (excellent) to F (critical issues).