{"title":"Timing Attack via Non-Constant-Time 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 verify_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(dev_id):\n    device_secrets = {\"sensor_001\": \"a8f3c9e2b7d1\", \"actuator_042\": \"9d2e1f4a6b8c\"}\n    return device_secrets.get(dev_id, \"default_key\")","explanation":"The code uses Python's standard string equality operator (==) to compare HMAC signatures, which performs character-by-character comparison and exits early on the first mismatch. This timing difference allows attackers to perform timing attacks to gradually deduce the correct signature by measuring response times for each byte position.","remediation":"The fix replaces the vulnerable string equality operator (==) with hmac.compare_digest(), which performs a constant-time comparison of the two strings. This ensures that the comparison takes the same amount of time regardless of how many characters match, preventing timing-based side-channel attacks that could allow an attacker to deduce the correct signature byte by byte.","secure_code":"import hmac\nimport hashlib\n\ndef verify_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(dev_id):\n    device_secrets = {\"sensor_001\": \"a8f3c9e2b7d1\", \"actuator_042\": \"9d2e1f4a6b8c\"}\n    return device_secrets.get(dev_id, \"default_key\")"}