Lesson 5 of 6
Lab 5 — Web Exploits and Secure Coding
Reproduce SQL injection and XSS on paper, then rewrite the vulnerable code properly.
Learn it
Most website hacks happen because the site treats what a user typed as an instruction instead of as data.
SQL injection means typing database commands into a login box. Cross-site scripting means typing JavaScript into a comment box.
The fix is never 'ban rude words' — it is to keep code and data in separate lanes.
Key terms
- SQL injection
- Injecting database commands through user input that is concatenated into a query.
- Parameterised query
- A query where values are sent separately from the SQL, so input can never become code.
- XSS
- Cross-site scripting — injecting JavaScript that runs in another user's browser.
- CSP
- Content-Security-Policy — a header restricting which scripts a page is allowed to run.
- Least privilege
- Giving each account only the permissions it genuinely needs.
Break it, then fix it
This login handler is vulnerable. Follow the attack, then the repair.
- 11. The code: query = "SELECT * FROM users WHERE name='" + name + "' AND pass='" + pw + "'"
- 22. The payload: The attacker types ' OR '1'='1' -- into the name box and anything into the password box.
- 33. The result: SELECT * FROM users WHERE name='' OR '1'='1' --' AND pass='x' — everything after -- is a comment, and '1'='1' is always true.
- 44. The impact: The first row is returned, which is usually the admin account. The attacker is now logged in without any password.
- 55. The wrong fix: Blocking apostrophes. Attackers use hex encoding, UNION SELECT, or second-order injection — and O'Brien can no longer log in.
- 66. The real fix: cur.execute("SELECT * FROM users WHERE name=%s AND pass_hash=%s", (name, hashed)) — the driver never lets the value alter the query structure.
- 77. Then go deeper: Give the web database user SELECT/INSERT only, hash passwords with Argon2, rate-limit logins, and log failed attempts.
Vulnerable vs safe
python# VULNERABLE — string concatenation
q = f"SELECT id FROM users WHERE email = '{email}'"
cur.execute(q)
# SAFE — parameterised
cur.execute("SELECT id FROM users WHERE email = %s", (email,))
# VULNERABLE — innerHTML with user data
# el.innerHTML = comment
# SAFE — treated as text, never parsed as HTML
# el.textContent = commentIn both cases the fix is the same idea: tell the interpreter explicitly that this is data, not code.
Sandbox lab
Practise the real technique in a fully simulated environment — no live systems, no real data, nothing leaves your browser.
Web exploit lab
A pretend vulnerable app running entirely in this page. No real database, no real network requests, no scripts are ever executed.
Try it
Secure-coding claims — true or false?
Escaping apostrophes is a complete defence against SQL injection.
Storing a session token in an HttpOnly cookie stops JavaScript from reading it.
Client-side validation is enough because the form checks the input first.
A strict Content-Security-Policy can block injected inline scripts even when XSS exists.
The web app's database account should have DROP TABLE rights so migrations work.
Challenge
Here is a real vulnerable endpoint: @app.get('/search') def search(): q = request.args['q'] rows = db.execute("SELECT title FROM posts WHERE title LIKE '%" + q + "%'").fetchall() return f"<h2>Results for {q}</h2>" + "".join(f"<li>{r[0]}</li>" for r in rows) Find BOTH vulnerabilities, give a working proof-of-concept input for each, rewrite the endpoint securely, and list two additional defence-in-depth controls.
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/7 clues revealed
- Clue 1 locked — reveal it only if you get stuck.
- Clue 2 locked — reveal it only if you get stuck.
- Clue 3 locked — reveal it only if you get stuck.
- Clue 4 locked — reveal it only if you get stuck.
- Clue 5 locked — reveal it only if you get stuck.
- Clue 6 locked — reveal it only if you get stuck.
- Clue 7 locked — reveal it only if you get stuck.
Each clue costs 8 XP (never below 38 XP). You'd earn 75 XP right now.
Extension: Explain how an attacker could chain the XSS with a CSRF request to change an admin's email address.