Step 2: Running Scan Commands Against a Server

Every type of scan that SSLyze can run against a server (supported cipher suites, Heartbleed, etc.) is represented by a ScanCommand. Once a ScanCommand is run against a server, it returns a “result” object with attributes containing the results of the scan command.

All the available ScanCommands and corresponding results are described in Appendix: Scan Commands.

Basic Example

The main class for running these commands is the Scanner class, which uses a pool of workers to run ScanCommand concurrently. It is very fast when scanning a large number of servers, and it has a rate-limiting mechanism to avoid DOS-ing a single server against which multiple ScanCommand are run at the same time.

The commands can be queued by passing a ServerScanRequest to the Scanner.queue_scan() method.

The results can later be retrieved using the Scanner.get_results() method, which returns an iterable of ServerScanResult. Each result is returned as soon as the server scan was completed.

A simple example on how to run some scan commands follows:

def basic_example() -> None:
    # Define the server that you want to scan
    server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup("www.google.com", 443)

    # Do connectivity testing to ensure SSLyze is able to connect
    try:
        server_info = ServerConnectivityTester().perform(server_location)
    except ConnectionToServerFailed as e:
        # Could not connect to the server; abort
        print(f"Error connecting to {server_location}: {e.error_message}")
        return

    # Then queue some scan commands for the server
    scanner = Scanner()
    server_scan_req = ServerScanRequest(
        server_info=server_info, scan_commands={ScanCommand.CERTIFICATE_INFO, ScanCommand.SSL_2_0_CIPHER_SUITES},
    )
    scanner.queue_scan(server_scan_req)

    # Then retrieve the results
    for server_scan_result in scanner.get_results():
        print(f"\nResults for {server_scan_result.server_info.server_location.hostname}:")

        # SSL 2.0 results
        ssl2_result = server_scan_result.scan_commands_results[ScanCommand.SSL_2_0_CIPHER_SUITES]
        print("\nAccepted cipher suites for SSL 2.0:")
        for accepted_cipher_suite in ssl2_result.accepted_cipher_suites:
            print(f"* {accepted_cipher_suite.cipher_suite.name}")

        # Certificate info results
        certinfo_result = server_scan_result.scan_commands_results[ScanCommand.CERTIFICATE_INFO]
        print("\nCertificate info:")
        for cert_deployment in certinfo_result.certificate_deployments:
            print(f"Leaf certificate: \n{cert_deployment.received_certificate_chain_as_pem[0]}")

Advanced Usage

The following script provides an example of running scan commands against multiple servers, and processing the results:

def main() -> None:
    # First validate that we can connect to the servers we want to scan
    servers_to_scan = []
    for hostname in ["cloudflare.com", "google.com"]:
        server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup(hostname, 443)
        try:
            server_info = ServerConnectivityTester().perform(server_location)
            servers_to_scan.append(server_info)
        except ConnectionToServerFailed as e:
            print(f"Error connecting to {server_location.hostname}:{server_location.port}: {e.error_message}")
            return

    scanner = Scanner()

    # Then queue some scan commands for each server
    for server_info in servers_to_scan:
        server_scan_req = ServerScanRequest(
            server_info=server_info, scan_commands={ScanCommand.CERTIFICATE_INFO, ScanCommand.SSL_2_0_CIPHER_SUITES},
        )
        scanner.queue_scan(server_scan_req)

    # Then retrieve the result of the scan commands for each server
    for server_scan_result in scanner.get_results():
        print(f"\nResults for {server_scan_result.server_info.server_location.hostname}:")

        # Scan commands that were run with no errors
        try:
            ssl2_result = server_scan_result.scan_commands_results[ScanCommand.SSL_2_0_CIPHER_SUITES]
            print("\nAccepted cipher suites for SSL 2.0:")
            for accepted_cipher_suite in ssl2_result.accepted_cipher_suites:
                print(f"* {accepted_cipher_suite.cipher_suite.name}")
        except KeyError:
            pass

        try:
            certinfo_result = server_scan_result.scan_commands_results[ScanCommand.CERTIFICATE_INFO]
            print("\nCertificate info:")
            for cert_deployment in certinfo_result.certificate_deployments:
                print(f"Leaf certificate: \n{cert_deployment.received_certificate_chain_as_pem[0]}")
        except KeyError:
            pass

        # Scan commands that were run with errors
        for scan_command, error in server_scan_result.scan_commands_errors.items():
            print(f"\nError when running {scan_command}:\n{error.exception_trace}")

Related Classes

class sslyze.Scanner(per_server_concurrent_connections_limit=None, concurrent_server_scans_limit=None)

The main class to use in order to call and schedule SSLyze’s scan commands from Python.

Parameters
  • per_server_concurrent_connections_limit (Optional[int]) –

  • concurrent_server_scans_limit (Optional[int]) –

queue_scan(server_scan)

Queue a server scan.

Parameters

server_scan (ServerScanRequest) –

Return type

None

get_results()

Return completed server scans.

Return type

Iterable[ServerScanResult]

class sslyze.ServerScanRequest(server_info, scan_commands, scan_commands_extra_arguments=<factory>)

A request to scan a specific server with the supplied scan commands.

Parameters
  • server_info (ServerConnectivityInfo) –

  • scan_commands (Set[Literal[‘certificate_info’, ‘ssl_2_0_cipher_suites’, ‘ssl_3_0_cipher_suites’, ‘tls_1_0_cipher_suites’, ‘tls_1_1_cipher_suites’, ‘tls_1_1_cipher_suites’, ‘tls_1_2_cipher_suites’, ‘tls_1_3_cipher_suites’, ‘tls_compression’, ‘tls_1_3_early_data’, ‘openssl_ccs_injection’, ‘tls_fallback_scsv’, ‘heartbleed’, ‘robot’, ‘session_renegotiation’, ‘session_resumption’, ‘session_resumption_rate’, ‘http_headers’]]) –

  • scan_commands_extra_arguments (ScanCommandExtraArgumentsDict) –

class sslyze.ServerScanResult(scan_commands_results, scan_commands_errors, server_info, scan_commands, scan_commands_extra_arguments)

The result of a ServerScanRequest that was completed by a Scanner.

Parameters
  • scan_commands_results (ScanCommandResultsDict) –

  • scan_commands_errors (Dict[Literal[‘certificate_info’, ‘ssl_2_0_cipher_suites’, ‘ssl_3_0_cipher_suites’, ‘tls_1_0_cipher_suites’, ‘tls_1_1_cipher_suites’, ‘tls_1_1_cipher_suites’, ‘tls_1_2_cipher_suites’, ‘tls_1_3_cipher_suites’, ‘tls_compression’, ‘tls_1_3_early_data’, ‘openssl_ccs_injection’, ‘tls_fallback_scsv’, ‘heartbleed’, ‘robot’, ‘session_renegotiation’, ‘session_resumption’, ‘session_resumption_rate’, ‘http_headers’], ScanCommandError]) –

  • server_info (ServerConnectivityInfo) –

  • scan_commands (Set[Literal[‘certificate_info’, ‘ssl_2_0_cipher_suites’, ‘ssl_3_0_cipher_suites’, ‘tls_1_0_cipher_suites’, ‘tls_1_1_cipher_suites’, ‘tls_1_1_cipher_suites’, ‘tls_1_2_cipher_suites’, ‘tls_1_3_cipher_suites’, ‘tls_compression’, ‘tls_1_3_early_data’, ‘openssl_ccs_injection’, ‘tls_fallback_scsv’, ‘heartbleed’, ‘robot’, ‘session_renegotiation’, ‘session_resumption’, ‘session_resumption_rate’, ‘http_headers’]]) –

  • scan_commands_extra_arguments (ScanCommandExtraArgumentsDict) –

class sslyze.ScanCommandResultsDict

A dictionary of results for every scan command that was scheduled against a specific server.

certificate_info: CertificateInfoScanResult = None
ssl_2_0_cipher_suites: CipherSuitesScanResult = None
ssl_3_0_cipher_suites: CipherSuitesScanResult = None
tls_1_0_cipher_suites: CipherSuitesScanResult = None
tls_1_1_cipher_suites: CipherSuitesScanResult = None
tls_1_2_cipher_suites: CipherSuitesScanResult = None
tls_1_3_cipher_suites: CipherSuitesScanResult = None
tls_compression: CompressionScanResult = None
tls_1_3_early_data: EarlyDataScanResult = None
openssl_ccs_injection: OpenSslCcsInjectionScanResult = None
tls_fallback_scsv: FallbackScsvScanResult = None
heartbleed: HeartbleedScanResult = None
robot: RobotScanResult = None
session_renegotiation: SessionRenegotiationScanResult = None
session_resumption: SessionResumptionSupportScanResult = None
session_resumption_rate: SessionResumptionRateScanResult = None
http_headers: HttpHeadersScanResult = None
sslyze.ScanCommandErrorsDict
class sslyze.ScanCommandErrorReasonEnum

An enumeration.

BUG_IN_SSLYZE = 1
CLIENT_CERTIFICATE_NEEDED = 2
CONNECTIVITY_ISSUE = 3
WRONG_USAGE = 4

Exporting to JSON

A ServerScanResult can be serialized to JSON using SSLyze’s special JsonEncoder.

class sslyze.JsonEncoder(*, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, sort_keys=False, indent=None, separators=None, default=None)

Special JSON encoder that can serialize any ServerScanResult returned by SSLyze.

A ServerScanResult can be serialized to JSON using the following code:

>>> from dataclasses import asdict
>>> import json
>>> import sslyze
>>>
>>> scanner = sslyze.Scanner()
>>> # Queue some ServerScanRequest... and then retrieve the results...
>>> for server_scan_result in scanner.get_results():
>>>     server_scan_result_as_json = json.dumps(asdict(server_scan_result), cls=sslyze.JsonEncoder)