Step 2: Running Scan Commands Against a Server

Every type of scan that SSLyze can run against a server (supported cipher suites, session renegotiation, etc.) is represented by a ScanCommand.

Once a ScanCommand is run against a server, it returns a ScanResult which is an object with attributes containing the results of the scan. The list of attributes and what they mean depends on what kind of scan was run (ie. which ScanCommand).

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

As explained in Step 1: Testing Connectivity to a Server, a properly initialized ServerConnectivityInfo is needed before the corresponding server can be scanned. Then, SSLyze can run ScanCommands against this server either:

  • Sequentially using the SynchronousScanner class.
  • Concurrently using the ConcurrentScanner class; this class is slightly more complex to use, but is also a lot faster when running a several ScanCommand and/or scanning multiple servers.

Running Commands Sequentially

Basic example

The SynchronousScanner class can be used to run ScanCommands against a server:

def demo_synchronous_scanner():
    # Run one scan command to list the server's TLS 1.0 cipher suites
    try:
        server_tester = ServerConnectivityTester(
            hostname='smtp.gmail.com',
            port=587,
            tls_wrapped_protocol=TlsWrappedProtocolEnum.STARTTLS_SMTP
        )
        print(f'\nTesting connectivity with {server_tester.hostname}:{server_tester.port}...')
        server_info = server_tester.perform()
    except ServerConnectivityError as e:
        # Could not establish an SSL connection to the server
        raise RuntimeError(f'Could not connect to {e.server_info.hostname}: {e.error_message}')

    command = Tlsv10ScanCommand()

    synchronous_scanner = SynchronousScanner()

    scan_result = synchronous_scanner.run_scan_command(server_info, command)
    for cipher in scan_result.accepted_cipher_list:
        print(f'    {cipher.name}')

The SynchronousScanner class

class sslyze.synchronous_scanner.SynchronousScanner(network_retries=3, network_timeout=5)

An object to run SSL scanning commands synchronously against a server.

__init__(network_retries=3, network_timeout=5)

Create a scanner for running scanning commands synchronously.

Parameters:
  • network_retries (int) – How many times SSLyze should retry a connection that timed out.
  • network_timeout (int) – The time until an ongoing connection times out.
Return type:

None

run_scan_command(server_info, scan_command)

Run a single scan command against a server; will block until the scan command has been completed.

Parameters:
  • server_info (ServerConnectivityInfo) – The server’s connectivity information. The test_connectivity_to_server() method must have been called first to ensure that the server is online and accessible.
  • scan_command (PluginScanCommand) – The scan command to run against this server.
Return type:

PluginScanResult

Returns:

The result of the scan command, which will be an instance of the scan command’s corresponding PluginScanResult subclass.

Running Commands Concurrently

Basic example

The ConcurrentScanner uses a pool of processes to run ScanCommands concurrently. It is very fast when scanning a large number of servers, and it has a dispatching mechanism to avoid DOS-ing a single server against which multiple ScanCommand are run at the same time.

The commands can be queued using the queue_scan_command() method, and the results can later be retrieved using the get_results() method:

def demo_concurrent_scanner():
    # Setup the server to scan and ensure it is online/reachable
    server_info = demo_server_connectivity_tester()

    # Run multiple scan commands concurrently. It is much faster than the SynchronousScanner
    concurrent_scanner = ConcurrentScanner()

    # Queue some scan commands
    print('\nQueuing some commands...')
    concurrent_scanner.queue_scan_command(server_info, Tlsv12ScanCommand())
    concurrent_scanner.queue_scan_command(server_info, CertificateInfoScanCommand())

    # Process the results
    print('\nProcessing results...')
    for scan_result in concurrent_scanner.get_results():
        # All scan results have the corresponding scan_command and server_info as an attribute
        print(f'\nReceived result for "{scan_result.scan_command.get_title()}" '
              f'on {scan_result.server_info.hostname}')

        # A scan command can fail (as a bug); it is returned as a PluginRaisedExceptionResult
        if isinstance(scan_result, PluginRaisedExceptionScanResult):
            raise RuntimeError(f'Scan command failed: {scan_result.scan_command.get_title()}')

        # Each scan result has attributes with the information yo're looking for
        # All these attributes are documented within each scan command's module
        if isinstance(scan_result.scan_command, Tlsv12ScanCommand):
            for cipher in scan_result.accepted_cipher_list:
                print(f'    {cipher.name}')

        elif isinstance(scan_result.scan_command, CertificateInfoScanCommand):
            # Print the Common Names within the verified certificate chain
            if not scan_result.verified_certificate_chain:
                print('Error: certificate chain is not trusted!')
            else:
                print('Certificate chain common names:')
                for cert in scan_result.verified_certificate_chain:
                    cert_common_names = cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)
                    print(f'   {cert_common_names[0].value}')

The ConcurrentScanner class

class sslyze.concurrent_scanner.ConcurrentScanner(network_retries=3, network_timeout=5, max_processes_nb=12, max_processes_per_hostname_nb=3)

An object to run SSL scanning commands concurrently by dispatching them using a pool of processes.

__init__(network_retries=3, network_timeout=5, max_processes_nb=12, max_processes_per_hostname_nb=3)

Create a scanner for running scanning commands concurrently using a pool of processes.

Parameters:
  • network_retries (int) – How many times SSLyze should retry a connection that timed out.
  • network_timeout (int) – The time until an ongoing connection times out.
  • max_processes_nb (int) – The maximum number of processes to spawn for running scans concurrently.
  • max_processes_per_hostname_nb (int) – The maximum number of processes that can be used for running scans concurrently against a single server. A lower value will reduce the chances of DOS-ing the server.
Return type:

None

queue_scan_command(server_info, scan_command)

Queue a scan command targeting a specific server.

Parameters:
  • server_info (ServerConnectivityInfo) – The server’s connectivity information. The test_connectivity_to_server() method must have been called first to ensure that the server is online and accessible.
  • scan_command (PluginScanCommand) – The scan command to run against this server.
Return type:

None

get_results()

Return the result of previously queued scan commands; new commands cannot be queued once this is called.

Return type:Iterable[PluginScanResult]
Returns:The results of all the scan commands previously queued. Each result will be an instance of the scan corresponding command’s PluginScanResult subclass. If there was an unexpected error while running the scan command, it will be a ‘PluginRaisedExceptionScanResult’ instance instead.
class sslyze.concurrent_scanner.PluginRaisedExceptionScanResult(server_info, scan_command, exception)

The result returned when a scan command threw an exception while being run by a ConcurrentScanner.

error_message

Text-formatted details about the exception that occurred.

Type:str