Skip to content

WeakCipher Validator

Checks that the connection negotiated a cipher suite on your allow-list. The built-in allow-list contains modern AEAD suites for TLS 1.2 plus three TLS 1.3 suites, so legacy or weak ciphers (RC4, 3DES, CBC-mode, anything with MD5) fail.

Opt-in

Enable via enabled_validators=["weak_cipher", ...] or ENABLED_VALIDATORS.

Try it

from certmonitor import CertMonitor

with CertMonitor("example.com", enabled_validators=["weak_cipher"]) as monitor:
    monitor.get_cert_info()
    print(monitor.validate()["weak_cipher"])

A strong negotiated suite passes:

These examples show selected fields from illustrative scans. validate() also adds status and code, described in the result contract.

{
  "is_valid": true,
  "cipher_suite": "TLS_AES_256_GCM_SHA384"
}

A suite outside the allow-list fails with a reason:

{
  "is_valid": false,
  "cipher_suite": "RC4-MD5",
  "reason": "Cipher suite RC4-MD5 is not allowed. Please update your allowed cipher suites or negotiate a supported cipher."
}

Customizing the allow-list

The built-in set is a library policy, not a live feed of external recommendations. Override it per call with the allowed_cipher_suites argument, passed through validator_args:

from certmonitor import CertMonitor

with CertMonitor("example.com", enabled_validators=["weak_cipher"]) as monitor:
    monitor.get_cert_info()
    result = monitor.validate(
        validator_args={
            "weak_cipher": {
                "allowed_cipher_suites": ["TLS_AES_256_GCM_SHA384", "ECDHE-RSA-AES256-GCM-SHA384"]
            }
        }
    )
    print(result["weak_cipher"])

TLS 1.2 and TLS 1.3 name suites differently

TLS 1.2 uses OpenSSL-style names (ECDHE-RSA-AES256-GCM-SHA384); TLS 1.3 uses IANA names (TLS_AES_256_GCM_SHA384). The default allow-list includes both families, so TLS 1.3 connections (the modern default) pass on their standard suites. If you supply a custom set, remember to include the TLS 1.3 names you expect to see.

This checks the connection CertMonitor negotiated. It does not enumerate every protocol or cipher the server would accept from a different client.

Reference

certmonitor.validators.weak_cipher.WeakCipherValidator

Bases: BaseCipherValidator

Validates that the negotiated cipher suite is in the allowed list.

The built-in allowed set contains modern AEAD suites. It is a library policy, not a live feed of external recommendations. Override it per call with the allowed_cipher_suites argument.

name class-attribute instance-attribute

name: str = 'weak_cipher'

validate

validate(cipher_info: dict[str, Any], host: str, port: int, *, allowed_cipher_suites: list[str] | None = None) -> WeakCipherResult

Validates that the negotiated cipher suite is in the allowed list.

Parameters:

Name Type Description Default
cipher_info dict

The cipher information.

required
host str

The hostname.

required
port int

The port number.

required
allowed_cipher_suites list

Override the default allowed cipher suites. When None (the default), the built-in allow-list is used.

None

Returns:

Name Type Description
dict WeakCipherResult

A dictionary containing the validation results, including whether the cipher suite is allowed.

Examples:

Example output (success): This example shows a connection using a strong cipher suite, so validation passes.

```json
{
    "is_valid": true,
    "cipher_suite": "ECDHE-RSA-AES128-GCM-SHA256"
}
```

Example output (failure): This example shows a connection using a weak cipher suite, so validation fails.

```json
{
    "is_valid": false,
    "cipher_suite": "TLS_RSA_WITH_RC4_128_MD5",
    "reason": "Cipher suite TLS_RSA_WITH_RC4_128_MD5 is not allowed. Please update your allowed cipher suites or negotiate a supported cipher."
}
```
Source code in certmonitor/validators/weak_cipher.py
def validate(
    self,
    cipher_info: dict[str, Any],
    host: str,
    port: int,
    *,
    allowed_cipher_suites: list[str] | None = None,
) -> WeakCipherResult:
    """
    Validates that the negotiated cipher suite is in the allowed list.

    Args:
        cipher_info (dict): The cipher information.
        host (str): The hostname.
        port (int): The port number.
        allowed_cipher_suites (list, optional): Override the default
            allowed cipher suites. When `None` (the default), the
            built-in allow-list is used.

    Returns:
        dict: A dictionary containing the validation results, including whether the cipher suite is allowed.

    Examples:
        Example output (success):
            This example shows a connection using a strong cipher suite, so validation passes.

            ```json
            {
                "is_valid": true,
                "cipher_suite": "ECDHE-RSA-AES128-GCM-SHA256"
            }
            ```

        Example output (failure):
            This example shows a connection using a weak cipher suite, so validation fails.

            ```json
            {
                "is_valid": false,
                "cipher_suite": "TLS_RSA_WITH_RC4_128_MD5",
                "reason": "Cipher suite TLS_RSA_WITH_RC4_128_MD5 is not allowed. Please update your allowed cipher suites or negotiate a supported cipher."
            }
            ```
    """
    allowed: frozenset[str] = (
        frozenset(allowed_cipher_suites)
        if allowed_cipher_suites is not None
        else _DEFAULT_ALLOWED_CIPHER_SUITES
    )

    cipher_suite = cipher_info.get("cipher_suite", {})
    cipher_name = cipher_suite.get("name")

    result: WeakCipherResult = {
        "is_valid": True,
        "cipher_suite": cipher_name,
    }

    if cipher_name not in allowed:
        result["is_valid"] = False
        result["reason"] = (
            f"Cipher suite {cipher_name} is not allowed. "
            "Please update your allowed cipher suites or negotiate a supported cipher."
        )

    return result