{"title":"Timing Attack via naive HMAC comparison","language":"Python","severity":"High","cwe":"CWE-208","source_lines":[6],"flow_lines":[6,7],"sink_lines":[7],"vulnerable_code":"import hmac\nimport hashlib\n\ndef validate_iot_device_signature(device_id, payload, received_sig):\n    secret_key = get_device_secret(device_id)\n    expected_sig = hmac.new(secret_key.encode(), payload.encode(), hashlib.sha256).hexdigest()\n    if received_sig == expected_sig:\n        return {\"authenticated\": True, \"device\": device_id}\n    return {\"authenticated\": False}\n\ndef get_device_secret(device_id):\n    device_secrets = {\"thermostat_01\": \"k8j2n4m9p1q5\", \"camera_02\": \"x7v3b6n2m8k4\"}\n    return device_secrets.get(device_id, \"default_secret\")","explanation":"The code uses a naive string equality comparison (==) to validate HMAC signatures instead of a constant-time comparison function. This allows timing attacks where an attacker can measure response times to determine correct signature bytes one at a time, eventually reconstructing a valid signature without knowing the secret key.","remediation":"The fix replaces the naive string equality comparison (==) with hmac.compare_digest(), which is a constant-time comparison function provided by Python's hmac module. This function takes the same amount of time regardless of how many bytes match, preventing an attacker from using timing measurements to progressively guess the correct signature byte by byte.","secure_code":"import hmac\nimport hashlib\n\ndef validate_iot_device_signature(device_id, payload, received_sig):\n    secret_key = get_device_secret(device_id)\n    expected_sig = hmac.new(secret_key.encode(), payload.encode(), hashlib.sha256).hexdigest()\n    if hmac.compare_digest(received_sig, expected_sig):\n        return {\"authenticated\": True, \"device\": device_id}\n    return {\"authenticated\": False}\n\ndef get_device_secret(device_id):\n    device_secrets = {\"thermostat_01\": \"k8j2n4m9p1q5\", \"camera_02\": \"x7v3b6n2m8k4\"}\n    return device_secrets.get(device_id, \"default_secret\")"}