# PYTHON-LANG-SEC-052: Deprecated ssl.wrap_socket() Usage

> **Severity:** MEDIUM | **CWE:** CWE-326 | **OWASP:** A02:2021

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

## Description

ssl.wrap_socket() is a deprecated convenience function that wraps a plain socket with
TLS. It was deprecated in Python 3.7 because it has limited configurability, poor
defaults, and does not provide the same level of TLS security control as the SSLContext
API.

The function has several security concerns: it defaults to ssl.PROTOCOL_TLS which
may allow older protocol versions, it does not enforce certificate verification by
default (unlike SSLContext(PROTOCOL_TLS_CLIENT)), and it provides limited control over
cipher suites, protocol versions, and other TLS parameters.

The replacement is to create an ssl.SSLContext() with the appropriate protocol constant,
configure it with minimum protocol version, cipher suites, and certificate requirements,
then call context.wrap_socket() on the socket.


## Vulnerable Code

```python
import ssl
import http.client
import requests as http_requests

# SEC-052: deprecated wrap_socket
wrapped = ssl.wrap_socket(sock)
```

## Secure Code

```python
import ssl
import socket

# INSECURE/DEPRECATED: ssl.wrap_socket()
# raw_socket = socket.create_connection((host, port))
# secure_socket = ssl.wrap_socket(raw_socket, server_hostname=host)

# SECURE: Use SSLContext.wrap_socket() with explicit configuration
def create_secure_client_socket(host: str, port: int) -> ssl.SSLSocket:
    ctx = ssl.create_default_context()
    # create_default_context() sets up verification and minimum TLS version
    raw_socket = socket.create_connection((host, port))
    return ctx.wrap_socket(raw_socket, server_hostname=host)

# SECURE: For server sockets with custom configuration
def create_secure_server_socket(
    bind_addr: str, port: int, certfile: str, keyfile: str
) -> ssl.SSLSocket:
    ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
    ctx.minimum_version = ssl.TLSVersion.TLSv1_2
    ctx.load_cert_chain(certfile=certfile, keyfile=keyfile)
    ctx.set_ciphers("ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20")
    raw_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    raw_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    raw_socket.bind((bind_addr, port))
    raw_socket.listen(5)
    return ctx.wrap_socket(raw_socket, server_side=True)

```

## Detection Rule (Python SDK)

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

class SSLModule(QueryType):
    fqns = ["ssl"]


@python_rule(
    id="PYTHON-LANG-SEC-052",
    name="Deprecated ssl.wrap_socket()",
    severity="MEDIUM",
    category="lang",
    cwe="CWE-326",
    tags="python,ssl,wrap-socket,deprecated,CWE-326",
    message="ssl.wrap_socket() is deprecated since Python 3.7. Use SSLContext.wrap_socket() instead.",
    owasp="A02:2021",
)
def detect_wrap_socket():
    """Detects deprecated ssl.wrap_socket() usage."""
    return SSLModule.method("wrap_socket")
```

## How to Fix

- Replace ssl.wrap_socket() with ssl.create_default_context().wrap_socket() for client connections, which enforces certificate verification and hostname checking.
- For server sockets, create an explicit SSLContext(PROTOCOL_TLS_SERVER) with minimum_version set to TLSv1_2, then call context.wrap_socket().
- Set ctx.minimum_version = ssl.TLSVersion.TLSv1_2 explicitly on all SSLContext objects to prevent negotiation of older protocol versions.
- Configure cipher suites via ctx.set_ciphers() to exclude weak algorithms including RC4, DES, and export-grade ciphers.
- Use ctx.load_verify_locations() or ssl.create_default_context(cafile=...) for custom CA certificates.

## Security Implications

- **Weak Default Configuration:** ssl.wrap_socket() may not enforce certificate verification or hostname checking by
default, depending on the Python version and how it is called. This can silently
create insecure TLS connections that appear to be secured.

- **No Minimum Protocol Version Enforcement:** Without an explicit SSLContext, ssl.wrap_socket() relies on OpenSSL defaults, which
may permit negotiation of TLS 1.0 or 1.1 in some configurations. An explicit context
allows setting minimum_version = ssl.TLSVersion.TLSv1_2.

- **Limited Cipher Suite Control:** The function parameters do not expose all cipher suite configuration options available
in SSLContext. Weak cipher suites from OpenSSL defaults may be negotiated without
explicit restriction.

- **Deprecated API with Reduced Security Maintenance:** Deprecated APIs receive less security review and may not benefit from future TLS
hardening improvements applied to the SSLContext API. Using SSLContext ensures access
to all current and future TLS security controls.


## FAQ

**Q: When was ssl.wrap_socket() deprecated and when will it be removed?**

ssl.wrap_socket() was deprecated in Python 3.7 (2018). It was marked for removal
but has not yet been removed as of Python 3.12. Code relying on it will eventually
break and should be migrated to SSLContext.wrap_socket() now to avoid future
compatibility issues.


**Q: What is the simplest migration path from ssl.wrap_socket()?**

For client code: replace ssl.wrap_socket(sock, server_hostname=host) with
ssl.create_default_context().wrap_socket(sock, server_hostname=host). This is
a one-line change that also improves security by enforcing certificate verification.
For server code: create an explicit SSLContext and call wrap_socket() on it.


**Q: Does ssl.create_default_context().wrap_socket() handle certificate verification?**

Yes. ssl.create_default_context() creates a context with check_hostname=True and
verify_mode=ssl.CERT_REQUIRED by default. The subsequent wrap_socket() call uses
these settings. This is more secure than ssl.wrap_socket() which does not enforce
these by default.


**Q: Is this rule about security or just about API modernization?**

Both. The immediate concern is API modernization since ssl.wrap_socket() is deprecated
and may be removed. The security concern is that ssl.wrap_socket() has weaker defaults
than SSLContext.wrap_socket() and provides less control over TLS configuration.
Migrating to SSLContext improves both correctness and security.


**Q: Are there third-party libraries that use ssl.wrap_socket() internally?**

Some older versions of HTTP and network libraries use ssl.wrap_socket(). If a
third-party library triggers this rule, check whether the library has been updated
and consider upgrading to a version that uses SSLContext. Report the issue to the
library maintainer if it has not been fixed.


**Q: How do I add client certificate authentication (mTLS) with SSLContext?**

Create the SSLContext, then call ctx.load_cert_chain(certfile=cert, keyfile=key)
before wrapping the socket. For servers requiring client certificates, also set
ctx.verify_mode = ssl.CERT_REQUIRED and ctx.load_verify_locations(cafile=client_ca).
This bidirectional certificate verification is not easily configurable with the
deprecated ssl.wrap_socket() API.


## References

- [CWE-326: Inadequate Encryption Strength](https://cwe.mitre.org/data/definitions/326.html)
- [Python docs: ssl.wrap_socket() deprecation](https://docs.python.org/3/library/ssl.html#ssl.wrap_socket)
- [Python docs: ssl.SSLContext](https://docs.python.org/3/library/ssl.html#ssl.SSLContext)
- [OWASP TLS Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Security_Cheat_Sheet.html)
- [OWASP Top 10 A02:2021 Cryptographic Failures](https://owasp.org/Top10/A02_2021-Cryptographic_Failures/)

---

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