EduSensei & Lurk
Lurk Lurk
Hey, EduSensei, I was thinking about how we can teach secure coding in a structured way—maybe we could dive into the common pitfalls in web apps and how to fix them. What do you think?
EduSensei EduSensei
That sounds like a great plan! Let’s break it into clear modules: 1) Input validation and sanitization, 2) Authentication & password handling, 3) Session and cookie security, 4) Error handling & logging, and 5) Secure deployment (patching, least privilege). For each module we list common pitfalls, explain why they’re dangerous, and give practical code examples and testing strategies. We’ll finish with a quick quiz to reinforce the concepts. Ready to start?
Lurk Lurk
Sounds solid. Let’s start with input validation and sanitization, but keep it tight and efficient—no fluff.We must adhere to character.Let’s kick off with input validation and sanitization, keeping the explanations concise and the examples focused.Great, I’ll draft the first module. Let's keep it tight—clear pitfalls, why they matter, and a couple of clean code snippets. I'll be ready for the next step.
EduSensei EduSensei
Great! Here’s a quick outline we can use for the first module: 1. **Common Pitfall:** Accepting raw input without checks. *Why it matters:* Leads to injection attacks, XSS, or data corruption. 2. **Solution:** - Define strict data types (int, string, enum). - Use whitelist validation: only allow expected patterns. - Escape output when rendering to HTML. 3. **Clean Code Example (Python + Flask):** ```python from flask import request, abort import re def safe_int(value): if re.fullmatch(r"\d+", value): return int(value) abort(400, "Invalid number") def safe_string(value): if re.fullmatch(r"[A-Za-z0-9_-]{1,50}", value): return value abort(400, "Invalid string") ``` 4. **Testing Tip:** Write unit tests that feed malicious payloads and confirm the validation rejects them. Let me know if you’d like to tweak any part or if we’re ready to move to authentication next.
Lurk Lurk
Looks tight—just a tiny tweak: maybe add a comment on why the regex pattern is chosen. Otherwise, we can roll into authentication. Let’s keep the momentum.
EduSensei EduSensei
Sure thing—here’s the tweak: ```python def safe_string(value): # Allow only letters, numbers, underscore, hyphen, 1–50 chars # This prevents injection and limits size for storage if re.fullmatch(r"[A-Za-z0-9_-]{1,50}", value): return value abort(400, "Invalid string") ``` Now we’re ready for the authentication module. Let’s keep it structured and efficient.
Lurk Lurk
Got it—next up: authentication & password handling. We’ll cover weak password policies, storing plain text, poor salt usage, and how to lock out after failures. Then give a clean Flask example with bcrypt, explain the flow, and a quick test idea. Let’s dive in.
EduSensei EduSensei
**Authentication & Password Handling – Quick Guide** 1. **Common Pitfalls** - Weak password policies (too short, no complexity). - Storing plain‑text passwords. - Reusing the same salt or none at all. - No account lockout or rate limiting after failed logins. 2. **Why They Matter** - Weak passwords let attackers guess easily. - Plain‑text means a database leak = instant compromise. - Reused or weak salts make rainbow‑table attacks trivial. - No lockout allows brute‑force or credential stuffing. 3. **Clean Flask Example (bcrypt)** ```python from flask import Flask, request, abort from werkzeug.security import check_password_hash, generate_password_hash import bcrypt import time app = Flask(__name__) # In‑memory user store (replace with DB in real app) users = {} failed_attempts = {} LOCKOUT_TIME = 300 # 5 minutes @app.route('/register', methods=['POST']) def register(): username = request.form['username'] pwd = request.form['password'] # Simple policy: min 8 chars, at least one digit if len(pwd) < 8 or not any(c.isdigit() for c in pwd): abort(400, "Password too weak") salt = bcrypt.gensalt() # unique per user pwd_hash = bcrypt.hashpw(pwd.encode(), salt) users[username] = pwd_hash return "Registered", 201 @app.route('/login', methods=['POST']) def login(): username = request.form['username'] pwd = request.form['password'] # Lockout check if username in failed_attempts: first_attempt, count = failed_attempts[username] if count >= 5 and time.time() - first_attempt < LOCKOUT_TIME: abort(429, "Too many attempts, try later") stored_hash = users.get(username) if stored_hash and bcrypt.checkpw(pwd.encode(), stored_hash): failed_attempts.pop(username, None) return "Login success" else: # Record failure if username in failed_attempts: first_attempt, count = failed_attempts[username] if time.time() - first_attempt < LOCKOUT_TIME: failed_attempts[username] = (first_attempt, count + 1) else: failed_attempts[username] = (time.time(), 1) else: failed_attempts[username] = (time.time(), 1) abort(401, "Invalid credentials") ``` 4. **Quick Test Idea** - Use `pytest` to simulate 6 rapid failed login attempts for a user. - Assert that the 6th attempt returns status 429 (lockout). - Wait for >5 minutes and verify login succeeds again. That covers the essentials—let me know if you’d like more detail or we can move on to sessions.
Lurk Lurk
Nice, that’s solid. Let’s push on to session and cookie security next—got a few tricks to keep the session state tight. Ready when you are.