In this activity, you will defeat Rainbow Table attacks by implementing Salted Hashing.
Scenario:
A simple hash (SHA-256) is not enough! Attackers use pre-computed tables (Rainbow Tables) to crack common passwords instantly. You must add a unique "Salt" to every password before hashing it.
Your Task
Modify the save_user function to secure the data:
Generate a Salt: Use secrets.token_hex(16) to create a random string.
Combine: Create a new string: salt + password.
Hash It: Hash that combined string using SHA-256.
Store Both: Save a dictionary into the database: {'salt': salt, 'hash': hashed_value}.
Why This Matters
By adding a random salt, two users with the same password ("password123") will have completely different hashes. This makes Rainbow Tables useless.
PYTHON EDITOR (storage_module.py)
1
SECURITY CONSOLE OUTPUT
Ready... Waiting for registration simulation.
from js import document, window
import sys
import hashlib
import secrets
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] Verifying Crypto Implementation...", "system")
user_code = document.getElementById("code-editor").value
# Pass required modules to the student's scope
test_env = {
'hashlib': hashlib,
'secrets': secrets
}
window.submissionStatus = "INCOMPLETE / FAILED"
try:
exec(user_code, test_env)
if "save_user" not in test_env:
log("[!] Error: Function 'save_user' not found.", "fail")
return
save_user = test_env["save_user"]
# --- TEST 1: Functional Check ---
log("--- TEST 1: Saving User 'Bob' ---", "system")
password_input = "securePass!@#"
result = save_user("Bob", password_input)
if not isinstance(result, dict):
log(" -> FAIL: Function must return a dictionary.", "fail")
return
if 'salt' not in result or 'hash' not in result:
log(" -> FAIL: Dictionary must contain keys 'salt' and 'hash'.", "fail")
return
log(" -> PASS: Data structure is correct.", "pass")
# --- TEST 2: Salt Randomness ---
log("--- TEST 2: Checking Salt Uniqueness ---", "system")
result2 = save_user("Bob", password_input)
if result['salt'] == result2['salt']:
log(" -> FAIL: Salt is not random! It didn't change on the second run.", "fail")
log(" Did you hardcode the salt?", "fail")
window.submissionStatus = "FAILED - Static Salt"
return
log(" -> PASS: Salt is random and unique.", "pass")
# --- TEST 3: Cryptographic Verification ---
log("--- TEST 3: Verifying Hash Integrity ---", "system")
stored_salt = result['salt']
stored_hash = result['hash']
# Calculate expected hash
expected_hash = hashlib.sha256((stored_salt + password_input).encode()).hexdigest()
# Check for "Encoding Only" mistake
encoded_only = (stored_salt + password_input).encode()
if stored_hash == encoded_only:
log(" -> FAIL: You encoded the bytes but forgot to HASH them!", "fail")
log(" [HINT] Wrap your code in hashlib.sha256(...).hexdigest()", "info")
window.submissionStatus = "FAILED - Not Hashed"
return
if stored_hash == expected_hash:
log(" -> PASS: Hash matches SHA-256(salt + password).", "pass")
log(" SUCCESS: Salted Hashing Implemented Correctly!", "pass")
window.submissionStatus = "PASSED - Secure"
else:
log(" -> FAIL: Hash mismatch.", "fail")
log(" Ensure you are hashing (salt + password).", "fail")
window.submissionStatus = "FAILED - Bad Hash"
except Exception as e:
log(f"[!] Runtime Error: {e}", "fail")
window.submissionStatus = "ERROR - Syntax/Runtime"