# Host Header Injection in Password Reset Link Generation

Language: Python
Severity: High
CWE: CWE-20

## Source
4

## Flow
4-5

## Sink
5

## Vulnerable Code
```python
def trigger_account_recovery(email_addr, request_obj):
    recovery_token = secrets.token_urlsafe(32)
    redis_cache.setex(f'recovery:{email_addr}', 1800, recovery_token)
    host_value = request_obj.headers.get('Host', 'default.app')
    reset_url = f'https://{host_value}/auth/recover?token={recovery_token}'
    email_body = f'Click here to recover your account: {reset_url}'
    smtp_client.send_mail(to=email_addr, subject='Account Recovery', body=email_body)
    return {'status': 'recovery_email_sent', 'email': email_addr}
```

## Explanation

The application accepts the Host header directly from the HTTP request without validation and embeds it into the password reset URL sent via email. An attacker can manipulate the Host header to inject a malicious domain, causing users to click reset links pointing to attacker-controlled servers where tokens can be captured.

## Remediation

The fix introduces a whitelist of allowed hostnames (configured via environment variable) and validates the incoming Host header against this list. If the Host header value is not in the allowed list, the application falls back to a trusted default host, preventing attackers from injecting arbitrary domains into the reset URL.

## Secure Code
```python
import os

ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', 'default.app').split(',')
DEFAULT_HOST = os.environ.get('APP_HOST', 'default.app')

def trigger_account_recovery(email_addr, request_obj):
    recovery_token = secrets.token_urlsafe(32)
    redis_cache.setex(f'recovery:{email_addr}', 1800, recovery_token)
    host_value = request_obj.headers.get('Host', DEFAULT_HOST)
    if host_value not in ALLOWED_HOSTS:
        host_value = DEFAULT_HOST
    reset_url = f'https://{host_value}/auth/recover?token={recovery_token}'
    email_body = f'Click here to recover your account: {reset_url}'
    smtp_client.send_mail(to=email_addr, subject='Account Recovery', body=email_body)
    return {'status': 'recovery_email_sent', 'email': email_addr}
```
