{"title":"Log Injection via Unsanitized Newline Characters in Python Logging","language":"Python","severity":"Medium","cwe":"CWE-117","source_lines":[5,8],"flow_lines":[5,9,6,9,7,9,8,9],"sink_lines":[9],"vulnerable_code":"import logging\nfrom flask import request, jsonify\n\nlogger = logging.getLogger('iot_device_manager')\n\ndef register_smart_device():\n    device_id = request.json.get('device_id', 'unknown')\n    device_type = request.json.get('device_type', 'generic')\n    firmware_ver = request.json.get('firmware_version', 'N/A')\n    owner_email = request.json.get('owner_email', '')\n    logger.info(f\"IoT Device Registration: ID={device_id}, Type={device_type}, Firmware={firmware_ver}, Owner={owner_email}\")\n    if device_id and device_type:\n        return jsonify({\"status\": \"registered\", \"device_id\": device_id})\n    return jsonify({\"status\": \"failed\", \"reason\": \"missing parameters\"}), 400","explanation":"User-controlled input from request.json (device_id, device_type, firmware_ver, owner_email) is directly interpolated into a logging statement without sanitization. An attacker can inject newline characters (\\n, \\r) to forge log entries, potentially hiding malicious activity or creating false audit trails.","remediation":"The fix introduces a sanitize_log_input() function that escapes newline characters (\\n, \\r) and other line-separator characters by replacing them with their escaped string representations. All user-controlled inputs are passed through this sanitization function before being included in the log message, preventing attackers from injecting fake log entries.","secure_code":"import logging\nfrom flask import request, jsonify\n\nlogger = logging.getLogger('iot_device_manager')\n\ndef sanitize_log_input(value):\n    \"\"\"Remove newline and carriage return characters to prevent log injection.\"\"\"\n    if not isinstance(value, str):\n        value = str(value)\n    return value.replace('\\n', '\\\\n').replace('\\r', '\\\\r').replace('\\x0b', '\\\\x0b').replace('\\x0c', '\\\\x0c').replace('\\x1e', '\\\\x1e').replace('\\x85', '\\\\x85')\n\ndef register_smart_device():\n    device_id = sanitize_log_input(request.json.get('device_id', 'unknown'))\n    device_type = sanitize_log_input(request.json.get('device_type', 'generic'))\n    firmware_ver = sanitize_log_input(request.json.get('firmware_version', 'N/A'))\n    owner_email = sanitize_log_input(request.json.get('owner_email', ''))\n    logger.info(f\"IoT Device Registration: ID={device_id}, Type={device_type}, Firmware={firmware_ver}, Owner={owner_email}\")\n    if device_id and device_type:\n        return jsonify({\"status\": \"registered\", \"device_id\": device_id})\n    return jsonify({\"status\": \"failed\", \"reason\": \"missing parameters\"}), 400"}