TLS misconfigurations are still one of the most common findings I write up in assessment reports — weak ciphers left enabled, expired or misissued certificates, missing security headers on the handshake side, renegotiation flaws that never got patched. SSLyze is the tool I reach for when I need a fast, thorough, and scriptable audit of a server’s SSL/TLS configuration rather than eyeballing openssl s_client output line by line.
This article covers what SSLyze does, how it’s architected, installation, full command syntax with real output, and how I use it across pentests, compliance audits, and incident response.
What SSLyze Does
SSLyze is a Python-based tool (with a fast C-based scanning engine, nassl, under the hood) that analyzes a server’s SSL/TLS configuration. It checks:
- Which SSL/TLS protocol versions are supported (SSLv2, SSLv3, TLS 1.0 through TLS 1.3)
- Which cipher suites are accepted per protocol
- Certificate chain validity, trust, expiration, and hostname matching
- Support for known-vulnerable configurations (Heartbleed, ROBOT, CCS injection, session renegotiation)
- OCSP stapling status
- HTTP security headers relevant to TLS (HSTS)
- Certificate transparency compliance
Architecture and Internal Working
SSLyze separates concerns into two layers:
nassl— a low-level Python binding around OpenSSL that lets SSLyze open connections at arbitrary protocol versions and cipher configurations, including some that modern OpenSSL builds disable by default (like SSLv2/SSLv3), which is essential for detecting genuinely legacy, vulnerable servers.- Scan commands (plugins) — each check (cipher suite support, certificate info, Heartbleed, etc.) is implemented as an independent “scan command” module. When you run a scan, SSLyze dispatches each requested scan command against the target concurrently using a thread pool, which is why it’s dramatically faster than testing each cipher manually with OpenSSL.
Results are aggregated into a single report, which can be output as human-readable text or structured JSON for automation.
Installation
Via pip (recommended):
pip install --user sslyze
Via Kali/Debian:
sudo apt update
sudo apt install sslyze -y
Verify:
sslyze --version
Basic Syntax
sslyze [options] target_host[:port]
Command Examples
1. Full default scan (recommended starting point)
sslyze example.com
This runs the full default suite: protocol support, cipher suites, certificate info, and common vulnerability checks all in one pass.
Sample output snippet:
TLS 1.2
------------------------------
Cipher Suites:
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 Preferred
TLS_RSA_WITH_AES_128_CBC_SHA
TLS 1.3
------------------------------
Cipher Suites:
TLS_AES_256_GCM_SHA384 Preferred
Certificate #1
------------------------------
Subject: example.com
Issuer: DigiCert Global G2 TLS RSA SHA256 2020 CA1
Validity: OK - Certificate is not expired
Hostname Validation: OK - Certificate matches server hostname
2. Checking a specific vulnerability
sslyze --heartbleed example.com
sslyze --robot example.com
3. Checking a non-standard port (e.g., mail server STARTTLS)
sslyze --starttls=smtp mail.example.com:587
4. Scanning multiple targets at once
sslyze example.com example.org:8443 10.0.0.5
5. Outputting JSON for automation/integration
sslyze --json_out=results.json example.com
This JSON can be parsed downstream to feed into a dashboard, ticketing system, or CI/CD security gate.
6. Checking only specific protocol support
sslyze --tlsv1_0 --tlsv1_1 example.com
This is useful for confirming whether legacy protocols that should have been disabled are still accepted.
7. Certificate-only scan
sslyze --certinfo example.com
Configuration and Tuning
--regularruns the standard default set of scan commands (protocol + cipher + cert + common vulns).--mozilla_config=intermediatecompares the discovered configuration against Mozilla’s recommended TLS configuration levels (modern, intermediate, old), which is a fast way to benchmark compliance.
sslyze --mozilla_config=intermediate example.com
Real-World Use Cases
PCI-DSS / compliance audits: Confirming that legacy TLS 1.0/1.1 and weak ciphers are actually disabled across a card-processing environment, with JSON output feeding directly into an audit report.
Penetration testing: Quickly flagging weak cipher suites, expired certs, or Heartbleed-vulnerable legacy servers during the external assessment phase.
Incident response: After a suspected TLS downgrade or MITM incident, running sslyze against affected servers to confirm current configuration hasn’t been tampered with or left in a weakened state.
DevSecOps / CI integration: Adding an sslyze --json_out step to a deployment pipeline so any new server that goes live with a bad TLS config fails the build automatically.
Workflow and Tool Integration
# Step 1: Discover live HTTPS hosts
amass enum -passive -d example.com | httpx -silent -ports 443 > https_hosts.txt
# Step 2: Run SSLyze against every discovered host
while read host; do
sslyze --json_out="${host}_sslyze.json" "$host"
done < https_hosts.txt
# Step 3: Parse JSON for weak ciphers across the fleet
python3 parse_sslyze_json.py *_sslyze.json
I usually pair sslyze findings with testssl.sh or sslscan output as a second opinion, since cross-validating cipher and protocol support across two independent tools catches edge cases either one alone might miss.
Performance Optimization
- Scan multiple hosts in one invocation (
sslyze host1 host2 host3) rather than looping shell calls where possible — SSLyze parallelizes internally. - Use
--json_outfor large fleets instead of parsing text output; it’s far more reliable for automation. - Skip
--roboton hosts you already know are patched and modern, since ROBOT checks add extra round-trips per cipher tested.
Troubleshooting
- Connection refused / timeout — confirm the port and protocol; STARTTLS-based services (SMTP, IMAP, FTP) need the
--starttls=flag rather than a direct TLS handshake. - “Could not connect” on legacy protocol checks — the target may be genuinely rejecting SSLv2/SSLv3, which is actually a good sign; verify with
openssl s_client -connect host:443 -ssl3as a cross-check. - Outdated
nasslerrors after a Python upgrade — reinstall viapip install --upgrade sslyzeto rebuild against your current Python version.
Best Practices and Common Mistakes
- Don’t stop at the default scan — explicitly test STARTTLS services (SMTP, IMAP) separately since they’re often overlooked.
- Cross-reference certificate expiration and chain trust findings against the actual certificate authority’s status, not just SSLyze’s output alone.
- Avoid treating “TLS 1.3 supported” as automatically secure — still check for weak fallback ciphers on TLS 1.2 that might be negotiated by older clients.
FAQ
Does SSLyze replace a manual OpenSSL check? For almost all practical purposes yes — it’s faster, more thorough, and easier to script, though a manual openssl s_client check is still handy for one-off debugging.
Can SSLyze test STARTTLS-based protocols like SMTP or IMAP? Yes, using the --starttls=<protocol> flag with the appropriate service name.
Is SSLyze safe to run against production systems? Generally yes — it’s a read-only configuration audit, not an exploitation tool, but always confirm scope and authorization first, especially with vulnerability-specific checks like Heartbleed.
Lab Example
Spin up an Nginx server in a lab VM with a deliberately weak config (TLS 1.0 enabled, weak ciphers allowed), then run:
sslyze --mozilla_config=intermediate 192.168.56.20
Compare the output against the Mozilla intermediate baseline to see exactly which settings need to change, then fix the Nginx config and re-scan to confirm remediation.
Summary
SSLyze turns what used to be a tedious, manual TLS audit into a fast, automatable, and thorough process. Whether I’m doing a one-off pentest or building continuous compliance checks into a pipeline, it gives me a reliable, structured view of exactly how a server’s TLS stack is configured — and exactly what needs fixing.
References
- Official GitHub repository: https://github.com/nabla-c0d3/sslyze
- Documentation: https://nabla-c0d3.github.io/sslyze/documentation
- Man page / help:
sslyze --help