{"title":"Host Header Injection in Password Reset Link Generation","language":"Python","severity":"High","cwe":"CWE-74","source_lines":[11],"flow_lines":[11,12],"sink_lines":[12],"vulnerable_code":"from flask import Flask, request\nimport smtplib\nfrom email.mime.text import MIMEText\n\napp = Flask(__name__)\n\n@app.route('/api/v2/account/recovery', methods=['POST'])\ndef initiate_account_recovery():\n    user_email = request.json.get('email')\n    reset_token = generate_secure_token(user_email)\n    host_domain = request.headers.get('Host')\n    recovery_url = f\"https://{host_domain}/reset-credentials?token={reset_token}\"\n    email_body = f\"Click here to reset your password: {recovery_url}\"\n    msg = MIMEText(email_body)\n    msg['Subject'] = 'Account Recovery Request'\n    msg['To'] = user_email\n    smtp_server.send_message(msg)\n    return {'status': 'recovery_email_sent'}, 200","explanation":"The application trusts the Host header from the HTTP request without validation and directly embeds it into the password reset URL sent via email. An attacker can manipulate the Host header to inject a malicious domain, causing password reset tokens to be sent to attacker-controlled servers where victims click the link.","remediation":"The fix introduces a whitelist of allowed/trusted host domains (ALLOWED_HOSTS) and validates the incoming Host header against this list before using it in URL construction. If the Host header does not match any trusted domain, the application falls back to a hardcoded DEFAULT_HOST, preventing attackers from injecting malicious domains into password reset links.","secure_code":"from flask import Flask, request\nimport smtplib\nfrom email.mime.text import MIMEText\n\napp = Flask(__name__)\n\n# Define allowed/trusted domains for password reset links\nALLOWED_HOSTS = ['www.example.com', 'example.com']\nDEFAULT_HOST = 'www.example.com'\n\n@app.route('/api/v2/account/recovery', methods=['POST'])\ndef initiate_account_recovery():\n    user_email = request.json.get('email')\n    reset_token = generate_secure_token(user_email)\n    host_domain = request.headers.get('Host', '')\n    # Validate the Host header against a whitelist of trusted domains\n    if host_domain not in ALLOWED_HOSTS:\n        host_domain = DEFAULT_HOST\n    recovery_url = f\"https://{host_domain}/reset-credentials?token={reset_token}\"\n    email_body = f\"Click here to reset your password: {recovery_url}\"\n    msg = MIMEText(email_body)\n    msg['Subject'] = 'Account Recovery Request'\n    msg['To'] = user_email\n    smtp_server.send_message(msg)\n    return {'status': 'recovery_email_sent'}, 200"}