{ "cells": [ { "cell_type": "markdown", "id": "4c05f3cb", "metadata": {}, "source": [ "# Day 4 โ€” Capstone: Build a Secure App + Ethics\n", "### Summer Coding & Cybersecurity Camp\n", "\n", "Final day! You'll see one of the most famous software vulnerabilities (and its fix),\n", "build a complete secure mini-app, and talk about what it means to use these skills\n", "**responsibly**.\n", "\n", "**By the end of today you can:**\n", "- Explain and *prevent* a classic injection vulnerability\n", "- Combine validation + hashing + error handling into one real app\n", "- Describe the ethics and laws of being a security person\n", "- Know where to go next ๐Ÿš€" ] }, { "cell_type": "markdown", "id": "1ae153d9", "metadata": {}, "source": [ "## 1. Warm-up: review the secure-code checklist\n", "From the first three days, a good program should:\n", "- โœ… **Validate input** (never trust it)\n", "- โœ… **Fail gracefully** with `try / except`\n", "- โœ… **Never store plain passwords** (hash + salt)\n", "- โœ… **Never hardcode secrets**\n", "- โœ… Limit things like login attempts\n", "\n", "Keep this list next to you for the capstone." ] }, { "cell_type": "markdown", "id": "a4afd61f", "metadata": {}, "source": [ "## 2. ๐Ÿ” The classic: SQL Injection (and how to stop it)\n", "Many apps store data in a **database** and ask for it with **SQL** queries. A\n", "huge real-world vulnerability happens when programmers glue user input directly\n", "into a query string. Let's build a tiny, safe sandbox database to *see* the problem โ€”\n", "then fix it. This is the #1 lesson every web developer must learn.\n", "\n", "> โš–๏ธ We do this on a throwaway in-memory database that belongs to us. You only ever\n", "> test security on systems **you own or are given permission to test** โ€” more on that\n", "> at the end of today." ] }, { "cell_type": "code", "execution_count": null, "id": "a2e22ed4", "metadata": {}, "outputs": [], "source": [ "import sqlite3\n", "\n", "# build a tiny pretend \"users\" database in memory (ours, disposable)\n", "db = sqlite3.connect(\":memory:\")\n", "db.execute(\"CREATE TABLE users (name TEXT, role TEXT)\")\n", "db.execute(\"INSERT INTO users VALUES ('sam', 'student')\")\n", "db.execute(\"INSERT INTO users VALUES ('admin', 'teacher')\")\n", "db.commit()\n", "print(\"Sandbox database ready.\")" ] }, { "cell_type": "markdown", "id": "3b201a76", "metadata": {}, "source": [ "### โŒ The vulnerable way (string concatenation)\n", "This builds the query by *pasting* user input straight in. Run it normally with a\n", "name like `sam`. Then run it again and enter this as the name:\n", "\n", "```\n", "x' OR '1'='1\n", "```\n", "\n", "Watch it dump **every** user โ€” the input changed the *meaning* of the query." ] }, { "cell_type": "code", "execution_count": null, "id": "f0cc3bd4", "metadata": {}, "outputs": [], "source": [ "# โŒ VULNERABLE โ€” for learning only, never write code like this\n", "def lookup_vulnerable(name):\n", " query = \"SELECT * FROM users WHERE name = '\" + name + \"'\"\n", " print(\"Query being run:\", query)\n", " return db.execute(query).fetchall()\n", "\n", "typed = input(\"Look up which user? \")\n", "print(\"Results:\", lookup_vulnerable(typed))" ] }, { "cell_type": "markdown", "id": "64b268f4", "metadata": {}, "source": [ "**Why it broke:** the input `x' OR '1'='1` turned the condition into something\n", "that is *always true*, so the database returned everyone. The user's text became\n", "**code**. That's injection.\n", "\n", "### โœ… The safe way (parameterized queries)\n", "Use a `?` placeholder and pass the value separately. Now the input is always treated\n", "as **data**, never as code โ€” even the same nasty input does nothing harmful. Try it!" ] }, { "cell_type": "code", "execution_count": null, "id": "e3469d5a", "metadata": {}, "outputs": [], "source": [ "# โœ… SAFE โ€” parameterized query\n", "def lookup_safe(name):\n", " return db.execute(\"SELECT * FROM users WHERE name = ?\", (name,)).fetchall()\n", "\n", "typed = input(\"Look up which user? \")\n", "print(\"Results:\", lookup_safe(typed))\n", "# Try x' OR '1'='1 again โ€” it safely finds nothing." ] }, { "cell_type": "markdown", "id": "cf761d6c", "metadata": {}, "source": [ "**The one rule to remember:** *Never build a query (or command) by pasting user\n", "input into a string. Always pass input as separate, escaped data.* The same idea\n", "protects against many \"injection\" attacks beyond SQL." ] }, { "cell_type": "markdown", "id": "77379d10", "metadata": {}, "source": [ "---\n", "## ๐Ÿงช CAPSTONE PROJECT โ€” Secure Account System\n", "Build a complete program that ties the whole week together. Requirements:\n", "\n", "**Register**\n", "- Ask for a username โ†’ **validate** it (3โ€“15 chars, alphanumeric)\n", "- Ask for a password โ†’ check **strength** (reuse your Day 3 checker)\n", "- Store only a **salted hash** (never the plain password)\n", "\n", "**Login**\n", "- Ask for username + password\n", "- Hash the attempt and compare; allow at most **3 attempts**\n", "- Use `try / except` so nothing crashes on weird input\n", "\n", "**Menu loop**\n", "- `1` Register `2` Login `3` Quit\n", "\n", "A starter skeleton is below. Fill in the `TODO`s. Solution is at the very end โ€”\n", "try hard first!" ] }, { "cell_type": "code", "execution_count": null, "id": "d78f309c", "metadata": {}, "outputs": [], "source": [ "# CAPSTONE starter\n", "import hashlib, secrets\n", "\n", "users = {} # username -> {\"salt\": ..., \"hash\": ...}\n", "\n", "def hash_with_salt(pw, salt):\n", " return hashlib.sha256((salt + pw).encode()).hexdigest()\n", "\n", "def is_valid_username(name):\n", " return 3 <= len(name) <= 15 and name.isalnum()\n", "\n", "def password_strength(pw):\n", " checks = [len(pw) >= 8,\n", " any(c.isdigit() for c in pw),\n", " any(c.isalpha() for c in pw),\n", " any(not c.isalnum() for c in pw)]\n", " return sum(checks) # 0-4\n", "\n", "def register():\n", " pass # TODO: validate username, check password strength, store salt + hash\n", "\n", "def login():\n", " pass # TODO: verify against stored salted hash, max 3 attempts\n", "\n", "# TODO: menu loop calling register() / login() / quit" ] }, { "cell_type": "markdown", "id": "22da0a22", "metadata": {}, "source": [ "## ๐Ÿ BONUS CHALLENGE โ€” Find & Fix\n", "Swap capstones with a partner. Try (politely!) to break their app with weird inputs\n", "and report bugs. Then fix every bug found in your own. This *find-and-fix* cycle is\n", "exactly what professional security testing looks like." ] }, { "cell_type": "markdown", "id": "59812c47", "metadata": {}, "source": [ "## 3. โš–๏ธ The ethics of security โ€” the most important slide\n", "Knowing how systems break is power. Using it well is what makes you a **security\n", "professional** instead of a criminal.\n", "\n", "- ๐ŸŽฉ **White hat:** finds weaknesses to *help* fix them, **with permission**.\n", "- ๐Ÿ•ถ๏ธ **Black hat:** breaks in to steal or damage โ€” this is **illegal**.\n", "- Only ever test systems you **own** or are **explicitly authorized** to test\n", " (your own laptop, practice labs, CTF competitions, bug-bounty programs).\n", "- Accessing someone else's computer/account without permission is a **crime** in\n", " almost every country โ€” even \"just looking.\"\n", "- If you find a real vulnerability, practice **responsible disclosure**: report it\n", " privately to the owner and give them time to fix it.\n", "- Protect people's data like you'd want yours protected.\n", "\n", "**Great ways to keep learning legally:** CTF (capture-the-flag) games, TryHackMe,\n", "picoCTF (made for students!), and your own home lab." ] }, { "cell_type": "markdown", "id": "6d0eaf63", "metadata": {}, "source": [ "## 4. ๐Ÿ“ Where to go next\n", "- Practice Python daily โ€” small projects beat big plans\n", "- Try **picoCTF** (beginner, student-friendly security puzzles)\n", "- Learn a bit about how websites work (HTTP, HTML)\n", "- Explore the **OWASP Top 10** โ€” the most common web vulnerabilities (you already\n", " met injection!)\n", "- Keep the secure-code checklist taped to your monitor ๐Ÿ˜„\n", "\n", "You started the week unable to print \"hello\" and finished building a salted, validated,\n", "injection-aware login system. That's a real foundation. **Congratulations!** ๐ŸŽ“" ] }, { "cell_type": "markdown", "id": "2acad9c6", "metadata": {}, "source": [ "---\n", "### โœ… Capstone solution (try yours first!)" ] }, { "cell_type": "code", "execution_count": null, "id": "9dc5987f", "metadata": {}, "outputs": [], "source": [ "# --- CAPSTONE solution ---\n", "import hashlib, secrets\n", "\n", "users = {}\n", "\n", "def hash_with_salt(pw, salt):\n", " return hashlib.sha256((salt + pw).encode()).hexdigest()\n", "\n", "def is_valid_username(name):\n", " return 3 <= len(name) <= 15 and name.isalnum()\n", "\n", "def password_strength(pw):\n", " checks = [len(pw) >= 8,\n", " any(c.isdigit() for c in pw),\n", " any(c.isalpha() for c in pw),\n", " any(not c.isalnum() for c in pw)]\n", " return sum(checks)\n", "\n", "def register():\n", " name = input(\"Choose a username: \")\n", " if not is_valid_username(name):\n", " print(\"โš ๏ธ Username must be 3-15 letters/numbers.\")\n", " return\n", " if name in users:\n", " print(\"โš ๏ธ That username is taken.\")\n", " return\n", " pw = input(\"Choose a password: \")\n", " if password_strength(pw) < 3:\n", " print(\"โš ๏ธ Password too weak (need length + letters + numbers + symbol).\")\n", " return\n", " salt = secrets.token_hex(8)\n", " users[name] = {\"salt\": salt, \"hash\": hash_with_salt(pw, salt)}\n", " print(f\"โœ… Account '{name}' created (only a salted hash was stored).\")\n", "\n", "def login():\n", " name = input(\"Username: \")\n", " if name not in users:\n", " print(\"โŒ No such user.\")\n", " return\n", " record = users[name]\n", " for attempt in range(3):\n", " guess = input(\"Password: \")\n", " if hash_with_salt(guess, record[\"salt\"]) == record[\"hash\"]:\n", " print(f\"โœ… Welcome back, {name}!\")\n", " return\n", " print(f\"โŒ Wrong. {2 - attempt} tries left.\")\n", " print(\"๐Ÿ”’ Locked out.\")\n", "\n", "while True:\n", " print(\"\\n1) Register 2) Login 3) Quit\")\n", " try:\n", " choice = input(\"Choose: \")\n", " except EOFError:\n", " break\n", " if choice == \"1\":\n", " register()\n", " elif choice == \"2\":\n", " login()\n", " elif choice == \"3\":\n", " print(\"Goodbye, and stay ethical! ๐ŸŽฉ\")\n", " break\n", " else:\n", " print(\"Please pick 1, 2, or 3.\")" ] } ], "metadata": { "colab": { "provenance": [], "toc_visible": true }, "kernelspec": { "display_name": "Python 3", "name": "python3" }, "language_info": { "name": "python" } }, "nbformat": 4, "nbformat_minor": 5 }