Databases & SQL

Lesson 7 of 7

Lesson 7 — Security, Performance and Real-World Practice

Injection, least privilege, indexes, query plans, backups — the things that keep a live database safe and fast.

🔴 Advanced 100 XP

Learn it

SQL injection happens when user input becomes part of the query. Parameterised queries fix it.

Least privilege: the app's account should only be able to do what the app needs — no DROP TABLE rights.

Row level security and views let different users see different rows of the same table.

Slow queries usually mean a missing index or a query that scans the whole table. EXPLAIN shows you which.

Key terms

SQL injection
Attack where user input is executed as part of a query.
Least privilege
Give each account only the permissions it truly needs.
Row level security
Policies limiting which rows a user can see or change.
Query plan
The steps the database will take; inspected with EXPLAIN.
Point-in-time recovery
Restoring a database to any moment before an incident.

Harden a live database

  1. 1Parameterise: Replace every string-built query with placeholders.
  2. 2Trim privileges: App user gets SELECT/INSERT/UPDATE on its tables only.
  3. 3Add policies: Row level security so a student reads only their own progress.
  4. 4Index the hot paths: EXPLAIN the three slowest queries and index the filtered columns.
  5. 5Prove the backup: Restore last night's backup into a scratch database and query it.

Injection, fixed

python# VULNERABLE — the input becomes code
name = request.form["name"]
cur.execute(f"SELECT * FROM student WHERE name = '{name}'")
#  name = "' OR '1'='1"  ->  returns every student

# SAFE — the input stays data
cur.execute("SELECT * FROM student WHERE name = %s", (name,))

# And in the database itself:
# CREATE POLICY own_rows ON progress
#   FOR SELECT TO app_user USING (user_id = current_setting('app.user_id')::uuid);

The parameterised version sends the query and the value separately, so the database never parses user input as SQL.

Try it

Safe practice or serious risk? Decide for each.

Building a WHERE clause by concatenating a search box value.

The web app connects as a user with only SELECT/INSERT/UPDATE on its own tables.

Storing passwords as bcrypt hashes with a per-user salt.

Taking backups nightly but never testing a restore.

Adding an index on every column just in case.

Challenge

A school portal query `SELECT * FROM grades WHERE student_id = ` + input takes 9 seconds and lets any logged-in student read anyone's grades. Write an incident plan: fix the injection, fix the access control, and fix the performance.

Pick whichever way suits you — every mode earns the same bonus XP.

Write at least 40 more characters to submit.

Mark your own work

Guided walkthrough — 0/3 clues revealed

  1. Clue 1 locked — reveal it only if you get stuck.
  2. Clue 2 locked — reveal it only if you get stuck.
  3. Clue 3 locked — reveal it only if you get stuck.

Each clue costs 5 XP (never below 25 XP). You'd earn 50 XP right now.

Extension: Add a composite index on (student_id, term) and explain why column order matters.

Quiz time

Question 1 of 5Score 0

The best defence against SQL injection is…