This vulnerability allows attackers to impersonate a trusted host.

Why is this an issue?

Transport Layer Security (TLS) provides secure communication between systems over the internet by encrypting the data sent between them. In this process, the role of hostname validation, combined with certificate validation, is to ensure that a system is indeed the one it claims to be, adding an extra layer of trust and security.

When hostname validation is disabled, the client skips this critical check. This creates an opportunity for attackers to pose as a trusted entity and intercept, manipulate, or steal the data being transmitted.

To do so, an attacker would obtain a valid certificate authenticating example.com, serve it using a different hostname, and the application code would still accept it.

What is the potential impact?

Establishing trust in a secure way is a non-trivial task. When you disable hostname validation, you are removing a key mechanism designed to build this trust in internet communication, opening your system up to a number of potential threats.

Identity spoofing

If a system does not validate hostnames, it cannot confirm the identity of the other party involved in the communication. An attacker can exploit this by creating a fake server and masquerading it as a legitimate one. For example, they might set up a server that looks like your bank’s server, tricking your system into thinking it is communicating with the bank. This scenario, called identity spoofing, allows the attacker to collect any data your system sends to them, potentially leading to significant data breaches.

How to fix it in Python Standard Library

Code examples

The following code contains examples of disabled hostname validation.

Certificate validation is not enabled by default when _create_unverified_context or _create_stdlib_context is used. It is recommended to use create_default_context instead. This method creates secure contexts that will check server hostnames and verify certificates (see {rule:python:S4830} for more information) by default.
If create_default_context cannot be used, then the check_hostname property must be set to True on the SSLContext. Doing so also automatically enables certificate verification for this context.

Noncompliant code example

import ssl

ctx1 = ssl.create_default_context()
ctx1.check_hostname = False  # Noncompliant

ctx2 = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)  # Noncompliant

Compliant solution

import ssl

# By default, hostname verification is enabled
ctx1 = ssl.create_default_context()

ctx2 = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
ctx2.check_hostname = True

How does this work?

To fix the vulnerability of disabled hostname validation, it is strongly recommended to first re-enable the default validation and fix the root cause: the validity of the certificate.

Use valid certificates

If a hostname validation failure prevents connecting to the target server, keep in mind that one system’s code should not work around another system’s problems, as this creates unnecessary dependencies and can lead to reliability issues.

Therefore, the first solution is to change the remote host’s certificate to match its identity. If the remote host is not under your control, consider replicating its service to a server whose certificate you can change yourself.

In case the contacted host is located on a development machine, and if there is no other choice, try following this solution:

How to fix it in OpenSSL

Code examples

Currently, pyOpenSSL does not provide any public method to enable hostname verification. Therefore, it is recommended to use another SSL library. The ssl module of the Python standard library can be a great replacement for several use cases.

Noncompliant code example

import socket
from OpenSSL import SSL

ctx = SSL.Context(SSL.TLSv1_2_METHOD)  # Noncompliant
ctx.set_verify(SSL.VERIFY_PEER)

conn = SSL.Connection(ctx, socket.socket(socket.AF_INET, socket.SOCK_STREAM))
conn.connect(("www.example.com", 443))
conn.send("GET / HTTP/1.0\r\nHost: www.example.com\r\n\r\n".encode())
result = conn.recv(1024)

conn.shutdown()
conn.close()

Compliant solution

import ssl
import socket

# By default, hostname verification is enabled
ctx = ssl.create_default_context()

with socket.create_connection(("www.example.com", 443)) as sock:
    with ctx.wrap_socket(sock, server_hostname="www.example.com") as conn:
        conn.send(f"GET / HTTP/1.0\r\nHost: www.example.com\r\n\r\n".encode())
        conn.recv(1024)

How does this work?

To fix the vulnerability of disabled hostname validation, it is strongly recommended to first re-enable the default validation and fix the root cause: the validity of the certificate.

Use valid certificates

If a hostname validation failure prevents connecting to the target server, keep in mind that one system’s code should not work around another system’s problems, as this creates unnecessary dependencies and can lead to reliability issues.

Therefore, the first solution is to change the remote host’s certificate to match its identity. If the remote host is not under your control, consider replicating its service to a server whose certificate you can change yourself.

In case the contacted host is located on a development machine, and if there is no other choice, try following this solution:

Resources

Standards