Application Security in Python
Day 1 – Introduction to Application Security
PYTHON TRACK
Refactory Academy · 2 Hours · Week 1, Monday
Today's 2-Hour Plan
0:25 – 0:55
0:00 – 0:25 0:55 – 1:35 1:35 – 2:00
Activity
Theory Practical Review
OWASP Top 10 + Case
CIA Triad & Why Security? Studies Spot the Vulnerability Discussion + Day 2 Preview
Learning Objectives
By the end of today, you will be able to:
• Explain the CIA Triad and give a real-world example of each
• List and briefly describe all OWASP Top 10 vulnerabilities
• Identify at least 3 security vulnerabilities in Python code snippets
• Explain why security must be part of development, not an afterthought
Opening Hook: The Uber Breach (2022)
An 18-year-old hacker sent an Uber employee a WhatsApp message pretending to be from Uber IT: "Hi, this
is Uber security. We've detected suspicious activity on your account. Please approve this login request."
The tired employee approved it. The hacker was inside Uber's entire internal system in minutes. They found
admin credentials stored in plain text on an internal wiki page.
Result: Access to Slack, AWS, Google Workspace, HackerOne, financial systems, and source code.
Lesson: Lesson: Social engineering + poor credential management = catastrophic breach. Two
fundamental security failures, zero technical exploit needed. Today we identify those mistakes — in your
Python code.
1. The CIA Triad – Foundation of All Security
Every security decision maps back to one or more of these three principles. This applies equally to
JavaScript, Python, or any other language.
Confidentiality Integrity Availability
Only the right people see the right Data is accurate and has not been Systems work when users need
data. tampered with. them.
A customer's NIN and deposit A product's selling price should only If someone floods the Nyondo Stock
balance should never be visible to a change if the Store Manager's API with fake requests, the Manager
sales attendant in Nyondo Stock. Python endpoint updates it. cannot check stock or issue receipts.
Quick Test – Which CIA principle was violated?
• A hacker reads all customer NIN numbers from the Nyondo database → Confidentiality
• A hacker modifies a Python config file to change admin permissions → Integrity
• The Nyondo Stock Python server crashes under a flood of requests → Availability
• An attacker intercepts an unencrypted API response with supplier credit data → Confidentiality +
Integrity
2. What is Application Security?
Definition: Application Security (AppSec) is the practice of finding, fixing, and preventing vulnerabilities
in software — from the moment you write the first line of code, through deployment, to maintenance.
In Python: whether you write a Django web app, a Flask API, a data processing script, or a CLI tool — all of
them can have security vulnerabilities. Nyondo Stock's Python backend is no exception.
The Cost of Fixing a Bug at Different Stages
Stage Cost to fix Example
During design UGX 3,600 Catch it in a code review before writing
During development UGX 36,000 Linter or peer review flags it
During testing UGX 360,000 Security test finds it before launch
After production UGX 3,600,000+ Patching, lawsuits, user notification
After a breach Billions / closure Equifax: UGX 2 trillion fine
Key Point: Key Point: Security is NOT a phase you do at the end. Python gives you powerful tools —
but it also lets you make powerful mistakes.
3. OWASP Top 10 (2021) – Applied to Nyondo Stock
(Python)
What is OWASP? OWASP (Open Web Application Security Project) publishes the world's most
referenced list of web security risks. These apply to Python apps (Django, Flask, FastAPI) just as much
as JavaScript apps.
Code Vulnerability Nyondo Stock Example
A01 Broken Access Control A sales attendant's Python request accesses /admin/reports without a role
check.
A02 Cryptographic Failures Storing supplier passwords or NIN numbers as plain text in the SQLite
database.
A03 Injection Building SQL queries with f-strings from the stock search form, or using
eval() on user input (Code Injection).
A04 Insecure Design The deposit scheme API requires no authentication at all — a design flaw
no implementation can fix.
Code Vulnerability Nyondo Stock Example
A05 Security Misconfiguration Django DEBUG=True in production, exposing stack traces with database
details.
A06 Outdated Components An old version of Pillow or Requests with a known CVE used in Nyondo's
backend. Run pip audit.
A07 Auth Failures No account lockout. Hardcoded credentials in [Link] committed to
GitHub.
A08 Integrity Failures Deserialising untrusted pickle files from suppliers. Installing packages
without verifying hashes.
A09 Logging Failures Not using Python's logging module for security events. No alert on
unusual stock queries.
A10 SSRF Nyondo Stock does [Link](user_url) with a supplier URL — attacker
points it at internal services.
Schedule: Deep Dive Schedule: A03 Injection (SQL + Python) → Day 2 | XSS (Python templating) →
Day 3 | A07 Auth Failures → Day 4 | A02 Cryptographic Failures → Day 4
4. Real-World Breaches
Equifax (2017) – 147 Million People Affected
Hackers exploited a known vulnerability in Apache Struts. A patch had been available for 2 months. The
same happens with Python packages.
• OWASP: A06 – Vulnerable & Outdated Components
• Python lesson: Always run pip audit. Check [Link] for known CVEs.
Codecov (2021) – Supply Chain Attack (Python-focused)
Attackers compromised the Codecov bash uploader script used in CI/CD pipelines. The malicious script
harvested environment variables — API keys, tokens, credentials — from thousands of builds.
• OWASP: A08 – Software Integrity Failures, A02 – Cryptographic Failures
• Python lesson: Never hardcode credentials. Use [Link](). Verify scripts you download and
execute.
British Airways (2018) – 500,000 Customers
Hackers injected a malicious script into the payment page that silently sent customer payment details to
attacker servers for 2 weeks.
• OWASP: A03 – Injection (XSS) + A09 – No Monitoring
• Python lesson: Template engines like Jinja2 have autoescaping — but only if you enable it. Never
disable it on user-provided content.
5. Practical: Spot the Vulnerability (Python)
Plain Python scripts — no Django or Flask needed. Run them if you like, or just read and analyse. All
examples use the Nyondo Stock system context. You only need Python installed.
Snippet 1 – Hardcoded Credentials
■ VULNERABLE: Credentials hardcoded in source code. Once pushed to any repository (even private
ones that later go public), they are compromised. Bots scan GitHub for these patterns within seconds of
a push.
# [Link] — accidentally pushed to GitHub
DATABASE_URL = 'sqlite:///nyondo_stock.db'
ADMIN_PASSWORD = 'admin123'
API_KEY = 'sk-live-a1b2c3d4e5f6g7h8'
SUPPLIER_DB = 'postgresql://admin:Secret@[Link]/nyondo'
# Bots scan GitHub for these patterns within SECONDS of a push.
Fix: Secure fix: use environment variables with [Link](). Add .env to .gitignore — never commit
it.
import os
DATABASE_URL = [Link]('DATABASE_URL')
ADMIN_PASSWORD = [Link]('ADMIN_PASSWORD')
API_KEY = [Link]('API_KEY')
# Add .env to .gitignore — never commit it
OWASP: A02 – Cryptographic Failures, A07 – Authentication Failures
Snippet 2 – Dangerous eval() in Discount Calculator
■ VULNERABLE: eval() executes arbitrary Python code. If user input reaches eval(), the attacker has
full control of your server — they can delete files, read credentials, or run any system command.
def calculate_discount(user_input):
result = eval(user_input) # NEVER do this
return result
# Normal: calculate_discount('price * 0.9') -> fine
# Attack: calculate_discount("__import__('os').system('rm -rf /')")
# -> deletes everything on the server
# Attack: calculate_discount("open('[Link]').read()")
# -> reads your credentials file
Fix: Secure fix: validate allowed characters and use [Link]() instead of eval() for math expressions.
import ast
def safe_calculate(user_input):
allowed = set('0123456789+-*/.() ')
if not all(c in allowed for c in user_input):
raise ValueError('Invalid characters')
tree = [Link](user_input, mode='eval')
return eval(compile(tree, '', 'eval'))
OWASP: A03 – Injection (Code Injection)
Snippet 3 – SQL Injection in Stock Search
■ VULNERABLE: User input concatenated directly into SQL using an f-string. The attacker controls the
query — they can extract all data or delete the entire stock table.
import sqlite3
def search_product(name):
conn = [Link]('nyondo_stock.db')
query = f"SELECT * FROM products WHERE name = '{name}'"
return [Link](query).fetchall()
# Attack: search_product("' OR '1'='1") -> returns ALL products
# Attack: search_product("'; DROP TABLE products; --") -> deletes stock
Fix: Secure fix: use parameterised queries with the ? placeholder. sqlite3 handles escaping
automatically.
import sqlite3
def search_product_safe(name):
conn = [Link]('nyondo_stock.db')
query = 'SELECT * FROM products WHERE name = ?'
return [Link](query, (name,)).fetchall()
# name is data, not code — sqlite3 escapes it automatically
OWASP: A03 – Injection (SQL Injection) — full coverage Day 2
Snippet 4 – Password Stored as Plain Text
■ VULNERABLE: Passwords stored in plain text. A database breach immediately exposes every user's
password. Many users reuse passwords — attacker now has their email, bank, and more. Storing plain
text passwords is illegal under GDPR.
def register_user(username, password):
[Link](
'INSERT INTO users (username, password) VALUES (?, ?)',
(username, password) # stored as plain text!
# Database stolen -> attacker reads every password directly.
# Storing plain text passwords is illegal under GDPR.
Fix: Secure principle (implemented fully on Day 4): use bcrypt to hash passwords. Store the hash, never
the password.
import bcrypt
def register_user_safe(username, password):
pw_hash = [Link]([Link]('utf-8'), [Link](rounds=12))
[Link](
'INSERT INTO users (username, password_hash) VALUES (?, ?)',
(username, pw_hash) # store hash, never the password
OWASP: A02 – Cryptographic Failures — full coverage Day 4
Snippet 5 – Path Traversal in File Reader
■ VULNERABLE: No path validation — user can traverse directories with ../../ to read any file on the
server. Full exception message also reveals server internals to the attacker.
def read_supplier_file(filename):
try:
with open(filename, 'r') as f:
return [Link]()
except Exception as e:
return f'Error: {e}' # reveals server path and file structure
# filename = '../../etc/passwd' -> path traversal
# Error reveals exact server path -> attacker adjusts and reads system files
Fix: Secure fix: resolve the real path with [Link]() and confirm it stays within the allowed
directory. Return only a generic message to the user — log full detail internally.
import os, logging
ALLOWED = '/var/www/nyondo/supplier-invoices/'
def read_supplier_file_safe(filename):
safe = [Link]([Link](ALLOWED, filename))
if not [Link](ALLOWED):
[Link](f'Path traversal attempt: {filename}')
return 'Access denied' # no details leaked
try:
with open(safe, 'r') as f: return [Link]()
except Exception:
[Link]('File read error', exc_info=True)
return 'File not found' # generic message to user
OWASP: A01 – Broken Access Control (Path Traversal)
6. Think Like an Attacker – STRIDE on Nyondo Stock
When you build any Python function or endpoint for Nyondo Stock, ask yourself these six questions:
Threat Nyondo Stock Question
S Spoofing Can a sales attendant's request pretend to be the Store Manager?
T Tampering Can someone change a product price or stock quantity they shouldn't?
R Repudiation Can an attendant deny making a fraudulent sale if there is no audit log?
I Info Disclosure Can a cashier see supplier credit terms or customer NIN numbers?
D Denial of Service Can someone flood the stock registration endpoint to block all operations?
E Elev. of Privilege Can a sales attendant access the Accounts/Admin report pages?
STRIDE Applied: Nyondo Stock – Sales Registration (Python)
Feature: A Python endpoint receives { product_id, quantity, payment_mode } to record a sale.
• Tampering: Attendant sends quantity=1 when selling 10 bags of cement. Fix: Always verify stock
server-side from the database before confirming the sale.
• Elevation of Privilege: Sales attendant calls /admin/supplier-credit directly. Fix: Role check on every
Python route — check [Link] before processing.
• DoS: Attacker floods the sale endpoint with 10,000 requests per second. Fix: Rate limiting on the Python
endpoint.
7. Discussion Questions
Q1. Nyondo Stock stores all user passwords as plain text in a SQLite file. Which CIA principle is most at risk?
Which OWASP category?
Q2. A developer uses [Link]('ping ' + user_input, shell=True). What can an attacker do? Which
OWASP category?
Q3. A Python developer says: "I'll add security once the Nyondo Stock app is done." Write 3 arguments
against this approach.
Q4. Code Review Challenge — how many security problems can you spot?
import sqlite3, os
def get_admin_data(user_id, table_name):
conn = [Link]('nyondo_stock.db')
query = f'SELECT * FROM {table_name} WHERE id = {user_id}'
result = [Link](query).fetchall()
return str(result) # returns raw DB content incl. password hashes
# Called with: get_admin_data([Link]['id'], [Link]['table'])
Key Takeaways – Day 1
• The CIA Triad (Confidentiality, Integrity, Availability) applies to all code — Python included
• Security bugs are exponentially more expensive to fix after deployment
• The OWASP Top 10 is the industry standard — know all 10 by name
• Never use eval() on user input — it executes arbitrary Python code
• Never use f-strings to build SQL queries — use parameterised queries with ?
• Never hardcode credentials in your code — use environment variables with [Link]()
• Always log security events internally — never expose exception details to the user
• Security is not a feature you add at the end — it's a habit from line one
Prepare for Day 2 – Injection Attacks (Python)
We go deep on SQL Injection and Input Validation using plain Python and sqlite3 tomorrow against the
Nyondo Stock database. No Django needed.
1. Make sure Python is installed and you can open a Python terminal
2. Try this yourself: create a small script that connects to sqlite3, creates a products table, and inserts one
product using an f-string — just to see what NOT to do
3. Think about this: why is [Link](query, (name,)) safer than [Link](f'...{name}...')?
Day 2 Preview: Day 2 Preview: We will write actual SQL injection attacks against a local Nyondo Stock
SQLite database and write the defences. We will look at how the product registration and supplier credit
forms can be exploited.
Refactory Academy – Application Security Module
Day 1 of 5 · Python Track · Nyondo Stock Project
"In Python, you have the power to do great things - and terrible ones."