{"title":"Timing Attack via Non-Constant-Time String Comparison in API Key Authentication","language":"Python","severity":"High","cwe":"CWE-208","source_lines":[9],"flow_lines":[9,11],"sink_lines":[11],"vulnerable_code":"import boto3\nfrom flask import request, jsonify\ndef validate_iot_device_token(req_ctx):\n    dev_token = req_ctx.headers.get('X-Device-Token', '')\n    svc = boto3.client('secretsmanager', region_name='us-east-1')\n    secret_blob = svc.get_secret_value(SecretId='iot/device-master-token')\n    master_tok = secret_blob['SecretString']\n    if dev_token == master_tok:\n        return jsonify({'status': 'authorized', 'scope': 'full'}), 200\n    return jsonify({'status': 'forbidden'}), 403","explanation":"The attacker-controlled token is read from the HTTP header at line 4 (dev_token) and compared against the master secret at line 8 using Python's built-in == operator. The == operator short-circuits on the first mismatched byte, meaning comparison time varies depending on how many leading characters match, allowing a timing oracle that lets an attacker brute-force the secret one character at a time by measuring response latency.","remediation":"Replaced the `==` operator on line 8 with `hmac.compare_digest()`, which performs a constant-time comparison of the two strings. This prevents timing side-channel attacks because the function always compares all bytes regardless of where a mismatch occurs, eliminating the information leakage that allowed character-by-character brute-forcing. Both values are explicitly encoded to bytes via UTF-8 to satisfy `compare_digest`'s preference for bytes-like objects.","secure_code":"import boto3\nimport hmac\nfrom flask import request, jsonify\ndef validate_iot_device_token(req_ctx):\n    dev_token = req_ctx.headers.get('X-Device-Token', '')\n    svc = boto3.client('secretsmanager', region_name='us-east-1')\n    secret_blob = svc.get_secret_value(SecretId='iot/device-master-token')\n    master_tok = secret_blob['SecretString']\n    if hmac.compare_digest(dev_token.encode('utf-8'), master_tok.encode('utf-8')):\n        return jsonify({'status': 'authorized', 'scope': 'full'}), 200\n    return jsonify({'status': 'forbidden'}), 403"}