Last Day
Your Twitter clone combines Python routing, SQL storage, and pages rendered with HTML, CSS, and Jinja2.
Web cookies are small files that a site stores in your browser to remember you between visits. The comic makes that term literal:
SQL injection
Before you demo the Twitter clone, check it for SQL injection.
The login route takes a username and password from a form and checks them against the database. An unsafe query pastes that input straight into a string:
# username and password came straight from the login form
cursor.execute(
"SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'"
)String concatenation makes the user’s username and password part of the SQL statement. Normal input works, but a user can also submit SQL syntax.
Suppose someone types bob'-- as their username. Our string becomes:
SELECT * FROM users WHERE username = 'bob'--' AND password = '...'The -- starts a SQL comment, so the database ignores the password check and runs only SELECT * FROM users WHERE username = 'bob'. That query returns Bob’s row and logs the attacker in without a password.
SQL injection can also modify or delete data, as in this xkcd example:
A mother names her son Robert'); DROP TABLE Students;--. When the school’s software pastes that name into a query the same way we just did, the '); ends the intended statement and DROP TABLE Students runs as a second command, deleting the student records. The school lost the year’s data because it trusted its input.
A company in the UK has a registered legal name that is a SQL injection string, chosen so that careless systems might break themselves just by storing it. A careless program can therefore interpret an official government record as SQL.
Never build queries by concatenating user input. Use SQLite’s ? placeholders instead:
cursor.execute(
"SELECT * FROM users WHERE username = ? AND password = ?",
(username, password),
)Each ? marks a value supplied separately in the tuple, so the database treats username and password as data rather than SQL. If someone sends bob'--, it looks for a user literally named with those six characters, finds none, and denies the login. Go through every execute call in your project and make sure not one of them concatenates user input.
Offense and defense
Defending the login requires understanding how an attacker breaks it.
The password-cracking lab demonstrated why you should use long passwords and never reuse one across important accounts. Be skeptical of confident security advice from someone who cannot write code: understanding how an attack works is part of defending against it.
Security testing can also be legal, paid work. Companies run bug bounty programs that pay researchers who find a vulnerability and report it privately. Stripe runs a bug bounty program, and platforms such as HackerOne connect companies with security researchers.
An optional extra-credit exercise uses Stripe’s old Capture the Flag competition, a ladder of websites with a planted vulnerability at each level. Level 3 of their 2012 CTF is a small Flask app with the SQL injection hole we just walked through. Download it, log in as the bob user without his password, and come explain how your exploit works. (It is old code that expects Python 2, so setup may take some work.) More competitions are listed at ctftime.org.
What you built
Automating tedious work is an old hacker-culture principle:
- Laziness is good.
- Boredom is evil.
- So automate the boring stuff.
You built and published a webpage from raw HTML and CSS (Project 0). You wrote a program that turns Markdown into HTML, a compiler of your own (Project 1). You sent Python out to read pages you did not write and pull data out of them by the thousands (Project 2). You built a tool that answers questions about your own documents, a small ChatGPT you control (Project 3), and then added tools that let it take actions (Project 4). And you finished by cloning Twitter (the final project), a database-backed web application that ties Python, Markdown, HTML, CSS, Jinja2, and SQL into one running program.
Programming remains difficult after the course. Debugging existing code often takes more time than writing it.
Code that is almost right usually fails, sometimes because of one wrong character. Debugging remains necessary with experience, and it is learnable. If you are curious about software-industry salaries, see levels.fyi.
Where to go next
This course concentrated on making programs work. Later computer science courses ask how to prove that a program is correct and what no computer can ever do, regardless of its speed. Mathematics provides precise answers to these questions:
Data structures and algorithms goes beyond code that runs: you learn to make it run well and to measure its efficiency. From there the field opens into the theory of computation, machine learning, systems, and security, with mathematics playing a larger role.
Keep automating
Keep using code for repetitive tasks: scrape data you copy by hand, script a rename you do every week, or build a small site. You can look up additional tools as you need them.
Thanks for a good term. Grab a byte.