# Timing Attack via Insecure String Comparison for API Keys

Language: Python
Severity: High
CWE: CWE-208

## Source
9

## Flow
9-11

## Sink
11

## Vulnerable Code
```python
import boto3
from flask import Flask, request, jsonify

app = Flask(__name__)
S3_ACCESS_TOKEN = "sk-prod-a8f3e9c2b1d4f7e6a5c8b9d2e1f4a7b3"

@app.route('/cloud/s3/upload', methods=['POST'])
def handle_s3_upload():
    client_token = request.headers.get('X-AWS-Proxy-Token', '')
    bucket_name = request.json.get('bucket')
    file_data = request.json.get('data')
    if client_token == S3_ACCESS_TOKEN:
        try:
            s3_client = boto3.client('s3')
            s3_client.put_object(Bucket=bucket_name, Key=request.json.get('filename'), Body=file_data)
            return jsonify({"status": "uploaded", "bucket": bucket_name}), 200
        except Exception as e:
            return jsonify({"error": str(e)}), 500
    else:
        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
```python
import boto3
import hmac
import os
from flask import Flask, request, jsonify

app = Flask(__name__)
S3_ACCESS_TOKEN = os.environ.get('S3_ACCESS_TOKEN', 'sk-prod-a8f3e9c2b1d4f7e6a5c8b9d2e1f4a7b3')

@app.route('/cloud/s3/upload', methods=['POST'])
def handle_s3_upload():
    client_token = request.headers.get('X-AWS-Proxy-Token', '')
    bucket_name = request.json.get('bucket')
    file_data = request.json.get('data')
    if hmac.compare_digest(client_token, S3_ACCESS_TOKEN):
        try:
            s3_client = boto3.client('s3')
            s3_client.put_object(Bucket=bucket_name, Key=request.json.get('filename'), Body=file_data)
            return jsonify({"status": "uploaded", "bucket": bucket_name}), 200
        except Exception as e:
            return jsonify({"error": str(e)}), 500
    else:
        return jsonify({"error": "invalid proxy token"}), 403
```
