{"title":"Timing Attack via Insecure String Comparison for API Keys","language":"Python","severity":"High","cwe":"CWE-208","source_lines":[9],"flow_lines":[9,11],"sink_lines":[11],"vulnerable_code":"import boto3\nfrom flask import Flask, request, jsonify\n\napp = Flask(__name__)\nS3_ACCESS_TOKEN = \"sk-prod-a8f3e9c2b1d4f7e6a5c8b9d2e1f4a7b3\"\n\n@app.route('/cloud/s3/upload', methods=['POST'])\ndef handle_s3_upload():\n    client_token = request.headers.get('X-AWS-Proxy-Token', '')\n    bucket_name = request.json.get('bucket')\n    file_data = request.json.get('data')\n    if client_token == S3_ACCESS_TOKEN:\n        try:\n            s3_client = boto3.client('s3')\n            s3_client.put_object(Bucket=bucket_name, Key=request.json.get('filename'), Body=file_data)\n            return jsonify({\"status\": \"uploaded\", \"bucket\": bucket_name}), 200\n        except Exception as e:\n            return jsonify({\"error\": str(e)}), 500\n    else:\n        return jsonify({\"error\": \"invalid proxy token\"}), 403","explanation":"The code uses a regular equality operator (==) to compare the client-provided token with the secret S3_ACCESS_TOKEN. This comparison is vulnerable to timing attacks because Python's == operator performs byte-by-byte comparison and exits early on the first mismatch, allowing attackers to measure response times and deduce the correct token character by character.","remediation":"The fix replaces the vulnerable == string comparison with hmac.compare_digest(), which performs a constant-time comparison that does not leak timing information about how many characters match. Additionally, the hardcoded secret token is moved to an environment variable to follow security best practices for secret management.","secure_code":"import boto3\nimport hmac\nimport os\nfrom flask import Flask, request, jsonify\n\napp = Flask(__name__)\nS3_ACCESS_TOKEN = os.environ.get('S3_ACCESS_TOKEN', 'sk-prod-a8f3e9c2b1d4f7e6a5c8b9d2e1f4a7b3')\n\n@app.route('/cloud/s3/upload', methods=['POST'])\ndef handle_s3_upload():\n    client_token = request.headers.get('X-AWS-Proxy-Token', '')\n    bucket_name = request.json.get('bucket')\n    file_data = request.json.get('data')\n    if hmac.compare_digest(client_token, S3_ACCESS_TOKEN):\n        try:\n            s3_client = boto3.client('s3')\n            s3_client.put_object(Bucket=bucket_name, Key=request.json.get('filename'), Body=file_data)\n            return jsonify({\"status\": \"uploaded\", \"bucket\": bucket_name}), 200\n        except Exception as e:\n            return jsonify({\"error\": str(e)}), 500\n    else:\n        return jsonify({\"error\": \"invalid proxy token\"}), 403"}