# Host Header Injection in Password Reset Link Generation

Language: Python
Severity: High
CWE: CWE-74

## Source
11

## Flow
11-12

## Sink
12

## Vulnerable Code
```python
from flask import Flask, request
import smtplib
from email.mime.text import MIMEText

app = Flask(__name__)

@app.route('/api/v2/account/recovery', methods=['POST'])
def initiate_account_recovery():
    user_email = request.json.get('email')
    reset_token = generate_secure_token(user_email)
    host_domain = request.headers.get('Host')
    recovery_url = f"https://{host_domain}/reset-credentials?token={reset_token}"
    email_body = f"Click here to reset your password: {recovery_url}"
    msg = MIMEText(email_body)
    msg['Subject'] = 'Account Recovery Request'
    msg['To'] = user_email
    smtp_server.send_message(msg)
    return {'status': 'recovery_email_sent'}, 200
```

## Explanation

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

## Remediation

The fix introduces a whitelist of allowed/trusted host domains (ALLOWED_HOSTS) and validates the incoming Host header against this list before using it in URL construction. If the Host header does not match any trusted domain, the application falls back to a hardcoded DEFAULT_HOST, preventing attackers from injecting malicious domains into password reset links.

## Secure Code
```python
from flask import Flask, request
import smtplib
from email.mime.text import MIMEText

app = Flask(__name__)

# Define allowed/trusted domains for password reset links
ALLOWED_HOSTS = ['www.example.com', 'example.com']
DEFAULT_HOST = 'www.example.com'

@app.route('/api/v2/account/recovery', methods=['POST'])
def initiate_account_recovery():
    user_email = request.json.get('email')
    reset_token = generate_secure_token(user_email)
    host_domain = request.headers.get('Host', '')
    # Validate the Host header against a whitelist of trusted domains
    if host_domain not in ALLOWED_HOSTS:
        host_domain = DEFAULT_HOST
    recovery_url = f"https://{host_domain}/reset-credentials?token={reset_token}"
    email_body = f"Click here to reset your password: {recovery_url}"
    msg = MIMEText(email_body)
    msg['Subject'] = 'Account Recovery Request'
    msg['To'] = user_email
    smtp_server.send_message(msg)
    return {'status': 'recovery_email_sent'}, 200
```
