# PYTHON-LANG-SEC-071: Paramiko Implicit Host Key Trust (AutoAddPolicy)

> **Severity:** HIGH | **CWE:** CWE-322 | **OWASP:** A02:2021

- **Language:** Python
- **Category:** Python Core
- **URL:** https://codepathfinder.dev/registry/python/lang/PYTHON-LANG-SEC-071
- **Detection:** `pathfinder scan --ruleset python/PYTHON-LANG-SEC-071 --project .`

## Description

paramiko.AutoAddPolicy() and paramiko.WarningPolicy() automatically accept SSH host
keys for servers not in the known_hosts file. With AutoAddPolicy, unknown keys are
silently accepted and added to known_hosts; with WarningPolicy, a warning is logged
but the connection proceeds.

Both policies undermine SSH's protection against man-in-the-middle attacks. SSH host
key verification ensures the client is connecting to the genuine server and not an
attacker impersonating it. Without this verification, an attacker who can intercept
or redirect the TCP connection can perform an MITM attack on the SSH session,
capturing credentials and all session data.

paramiko.RejectPolicy() (the default if no policy is set) rejects connections to
servers with unknown host keys and is the secure choice. Alternatively, pre-populate
known_hosts with the expected host keys.


## Vulnerable Code

```python
import socket
import paramiko
import multiprocessing.connection

client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
```

## Secure Code

```python
import paramiko
import os

# INSECURE: AutoAddPolicy or WarningPolicy
# ssh = paramiko.SSHClient()
# ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

# SECURE: Use RejectPolicy with pre-loaded known_hosts
def create_ssh_client(host: str, username: str, key_path: str) -> paramiko.SSHClient:
    ssh = paramiko.SSHClient()
    # RejectPolicy is the default, but set it explicitly for clarity
    ssh.set_missing_host_key_policy(paramiko.RejectPolicy())
    # Load system and user known_hosts
    ssh.load_system_host_keys()
    ssh.load_host_keys(os.path.expanduser("~/.ssh/known_hosts"))
    ssh.connect(host, username=username, key_filename=key_path)
    return ssh

# SECURE: With explicit expected host key (certificate pinning approach)
def create_pinned_ssh_client(host: str, username: str,
                             key_path: str, expected_key_file: str) -> paramiko.SSHClient:
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.RejectPolicy())
    # Load only the expected host key
    ssh.load_host_keys(expected_key_file)
    ssh.connect(host, username=username, key_filename=key_path)
    return ssh

```

## Detection Rule (Python SDK)

```python
from rules.python_decorators import python_rule
from codepathfinder import calls, QueryType

class ParamikoModule(QueryType):
    fqns = ["paramiko"]


@python_rule(
    id="PYTHON-LANG-SEC-071",
    name="Paramiko Implicit Trust Host Key",
    severity="HIGH",
    category="lang",
    cwe="CWE-322",
    tags="python,paramiko,ssh,host-key,mitm,CWE-322",
    message="AutoAddPolicy/WarningPolicy trusts unknown host keys. Use RejectPolicy or verify keys.",
    owasp="A02:2021",
)
def detect_paramiko_trust():
    """Detects paramiko AutoAddPolicy and WarningPolicy usage."""
    return ParamikoModule.method("AutoAddPolicy", "WarningPolicy", "set_missing_host_key_policy")
```

## How to Fix

- Replace AutoAddPolicy() and WarningPolicy() with RejectPolicy(), which rejects connections to servers with unknown host keys.
- Pre-populate known_hosts with expected server host keys using ssh-keyscan in your deployment pipeline or configuration management.
- Load known host keys using ssh.load_system_host_keys() and ssh.load_host_keys(known_hosts_path) before connecting.
- For automated infrastructure where host keys change (auto-scaling), use SSH certificate authorities instead of individual host keys.
- Never suppress host key warnings in production code; treat unknown host keys as a potential MITM attack indicator.

## Security Implications

- **SSH Session MITM via Key Substitution:** An attacker who can intercept or redirect the SSH connection can present their own
host key instead of the server's. With AutoAddPolicy, the client accepts and uses
the attacker's key, decrypting all session data and enabling command injection into
the session.

- **Credential Theft:** During the SSH authentication phase, the client sends its username and authenticates
(password or key challenge-response). With an MITM SSH session, the attacker
captures the authentication credentials and can reuse them to authenticate directly
to the real server.

- **Command Injection in Automated Workflows:** Python scripts that automate SSH commands (deployment, configuration management,
monitoring) with AutoAddPolicy trust the connection unverified. An MITM attacker
can inject malicious commands into the session, executing them with the SSH user's
privileges on the target server.

- **Known_hosts Pollution:** AutoAddPolicy permanently adds attacker-controlled keys to known_hosts. Future
connections using RejectPolicy will then trust the attacker's key, enabling ongoing
MITM attacks even after the original vulnerability is noticed.


## FAQ

**Q: Why is AutoAddPolicy used so commonly if it is insecure?**

AutoAddPolicy is used because it avoids the friction of host key management in
development and CI/CD environments. The first connection to a new server raises
a paramiko.ssh_exception.SSHException when using RejectPolicy unless the key has
been pre-loaded. Developers use AutoAddPolicy as a quick fix. The correct solution
is to pre-populate known_hosts in deployment automation.


**Q: How do I pre-populate known_hosts in a CI/CD pipeline?**

Use ssh-keyscan to fetch the server's host key and add it to known_hosts:
ssh-keyscan -H hostname >> ~/.ssh/known_hosts. In GitHub Actions, use the
ssh-action with host_key_check: true. In Ansible, use ssh-keyscan before
the playbook runs. Pre-populate known_hosts at deployment time, not at runtime.


**Q: Is there a safe way to handle the first connection to a new server?**

Yes. During infrastructure provisioning, fetch the expected host key via a secure
out-of-band channel (the cloud provider API, a configuration management secret,
or an SSH CA certificate). Write it to known_hosts or a custom trusted key file
before the first paramiko connection. Never trust a key received from the same
network path as the SSH connection.


**Q: What is an SSH Certificate Authority and how does it help?**

An SSH CA signs host certificates that include the hostname. Clients trust the CA
key, not individual host keys. This eliminates the need to distribute and update
individual host keys for auto-scaling infrastructure. paramiko supports SSH
certificate authentication. See paramiko's documentation for @cert-authority in
known_hosts.


**Q: Does RejectPolicy throw an exception on unknown hosts?**

Yes. paramiko.RejectPolicy() raises paramiko.ssh_exception.SSHException with the
message "Server {hostname!r} not found in known_hosts" when the server's key is
not in known_hosts. Catch this exception and handle it as a security event, not
a routine error to be suppressed.


**Q: Is it safe to use WarningPolicy if I monitor the logs?**

No. WarningPolicy still proceeds with the connection despite the unknown key,
providing no actual protection against MITM. The warning may be missed, ignored,
or logged after the damage is done. Use RejectPolicy and treat unknown host keys
as security incidents, not warnings to be monitored.


## References

- [CWE-322: Improper Certificate Validation](https://cwe.mitre.org/data/definitions/322.html)
- [paramiko docs: MissingHostKeyPolicy](https://docs.paramiko.org/en/stable/api/client.html#paramiko.client.MissingHostKeyPolicy)
- [OWASP Top 10 A02:2021 Cryptographic Failures](https://owasp.org/Top10/A02_2021-Cryptographic_Failures/)
- [OWASP TLS Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Security_Cheat_Sheet.html)
- [SSH Host Key Verification Best Practices](https://www.ssh.com/academy/ssh/host-key)

---

Source: https://codepathfinder.dev/registry/python/lang/PYTHON-LANG-SEC-071
Code Pathfinder — Open source, type-aware SAST with cross-file dataflow analysis
