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.
- AI-generated code is inherently more secure than human-written code because AI learns from best practices.
- Blacklist validation is preferred over whitelist validation because it catches more attacks.
- Parameterized queries prevent SQL injection by separating SQL code from data.
- Storing JWTs in localStorage is a recommended practice because it persists across browser sessions.
- The
HttpOnlyflag on a cookie prevents JavaScript from reading the cookie's value. - 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.
- A database user account has only SELECT and INSERT permissions, not DELETE or DROP.
- The application validates input at the API layer and again at the database layer.
- A function checks the JWT signature, verifies the expiration, and confirms the issuer claim.
- A microservice communicates only on the ports it needs and has no access to other services' databases.
- 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:
- A user changes the URL from
/api/orders/42to/api/orders/43and sees another user's order. - The application uses
pickle.loads()on user-supplied data. - An error page displays a full stack trace including database connection strings.
- The application does not enforce rate limiting on its password reset endpoint.
- 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:
- API keys are passed as query parameters:
GET /api/data?key=abc123 - User passwords are encrypted (not hashed) so they can be recovered for support purposes.
- All microservices share a single database with a single database user account.
- Error responses include the full Python traceback in development mode, which is also the mode running in production because "it makes debugging easier."
- CORS is set to
Access-Control-Allow-Origin: *to support mobile apps. - 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.
- bcrypt with work factor 12
- Argon2id with memory=65536 KB, iterations=3, parallelism=4
- scrypt with N=2^14, r=8, p=1
- 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:
.envfiles withpython-dotenv(development + production)- AWS Secrets Manager for production,
.envfor development - HashiCorp Vault for all environments
- Doppler for all environments
- 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:
- Fix the code to use cryptographically secure random tokens.
- Deploy the fix to production.
- 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).