>
Software Security Training

Exercise: The Brute Force Balance

In this activity, you will balance Security (stopping attackers) vs. Usability (allowing user mistakes) by implementing a lockout policy.

Scenario: Attackers are running a "Brute Force" script against your login page, trying thousands of passwords per second. Your current code allows infinite attempts.

Your Task

Modify the check_login function to enforce these rules:

  1. Check History: If the user has 3 or more failed attempts, return "LOCKED" immediately.
  2. On Fail: If the password is wrong, increment the failure count and return "FAIL".
  3. On Success: If the password is correct, reset the failure count to 0 (Usability!) and return "SUCCESS".

Why This Matters

If you don't reset the count on success, a valid user who made a typo yesterday might get locked out today. This illustrates the syllabus goal of "balancing usability and security."

PYTHON EDITOR (auth_module.py)
1
SECURITY CONSOLE OUTPUT
Ready... Waiting for login simulation.
from js import document, window import sys def setup(): loading = document.getElementById("loading") if loading: loading.style.display = "none" setup() class ConsoleOutput: def write(self, text): if text.strip(): log(text.strip(), "normal") def flush(self): pass sys.stdout = ConsoleOutput() sys.stderr = ConsoleOutput() def log(message, type="normal"): console = document.getElementById("console-output") if type == "clear": console.innerHTML = "" return color = "#ffffff" if type == "pass": color = "#2ecc71" if type == "fail": color = "#e74c3c" if type == "info": color = "#3498db" if type == "system": color = "#d4d4d4" safe_message = message.replace("<", "<").replace(">", ">") console.innerHTML += f'
{safe_message}
' console.scrollTop = console.scrollHeight # --- TEST RUNNER --- def run_tests(*args): log("", "clear") log("[SYSTEM] Starting Authentication Simulator...", "system") user_code = document.getElementById("code-editor").value # Helper to create fresh user for each test run def get_fresh_user(): return {'failed_attempts': 0} test_env = {} window.submissionStatus = "INCOMPLETE / FAILED" try: # Execute in test_env exec(user_code, test_env) if "check_login" not in test_env: log("[!] Error: Function 'check_login' not found.", "fail") return check_login = test_env["check_login"] # --- TEST 1: Normal Login --- log("--- TEST 1: Valid Login ---", "system") u1 = get_fresh_user() res1 = check_login("secret123", "secret123", u1) if res1 == "SUCCESS": log(" -> PASS: Login successful.", "pass") else: log(f" -> FAIL: Expected 'SUCCESS', got '{res1}'", "fail") return # --- TEST 2: Lockout Logic --- log("--- TEST 2: Brute Force Simulation ---", "system") u2 = get_fresh_user() check_login("wrong1", "secret", u2) if u2['failed_attempts'] != 1: log(" -> FAIL: You didn't increment failed_attempts.", "fail") return log(" -> Attempt 1 Failed (Count: 1)") check_login("wrong2", "secret", u2) log(" -> Attempt 2 Failed (Count: 2)") check_login("wrong3", "secret", u2) log(" -> Attempt 3 Failed (Count: 3)") res_locked = check_login("wrong4", "secret", u2) if res_locked == "LOCKED": log(" -> PASS: Account successfully locked.", "pass") else: log(f" -> FAIL: Security hole! Expected 'LOCKED', got '{res_locked}'", "fail") window.submissionStatus = "FAILED - Vulnerable" return # --- TEST 3: Usability --- log("--- TEST 3: Usability (Reset on Success) ---", "system") u3 = get_fresh_user() u3['failed_attempts'] = 2 check_login("secret", "secret", u3) if u3['failed_attempts'] == 0: log(" -> PASS: Failure count reset to 0.", "pass") log(" SUCCESS: Security and Usability balanced.", "pass") window.submissionStatus = "PASSED - Secure" else: log(f" -> FAIL: Bad Usability! Count is {u3['failed_attempts']}.", "fail") window.submissionStatus = "FAILED - Poor Usability" except Exception as e: # --- SMART HINT SYSTEM --- # Catches the specific error for 'failed_attempts' error_msg = str(e) if "name 'failed_attempts' is not defined" in error_msg: log(f"[!] HINT: 'failed_attempts' is not a variable.", "info") log(f" It is inside the dictionary 'user_data'.", "info") log(f" Try this: user_data['failed_attempts']", "info") else: log(f"[!] Runtime Error: {e}", "fail") window.submissionStatus = "ERROR - Syntax/Runtime"