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¶
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}')