How to check cipher suites of a server

Offer one family at a time and read what comes back: openssl s_client -connect example.com:443 -servername example.com -tls1_2 -cipher 'kRSA@SECLEVEL=0' </dev/null 2>&1. A Cipher : line names a suite the server accepted. An SSL alert number 40 line means the server declined that whole family.

Why check this

The negotiated suite tells you what one client got. The accepted set tells you what the weakest client can get, and that is the number a security review asks for. Run this after a TLS profile change and before a release that adds a payment or health data path. A concrete failure it prevents: an origin left with RSA key exchange enabled, so a recorded session can be decrypted later by anyone who obtains the private key, while every browser test looked clean because browsers pick the strong suite.

OpenSSL has no single command that enumerates a remote server's list. The protocol never publishes it. The ServerHello names one suite, the one chosen for this connection, so the only way to learn the rest is to narrow your own offer and repeat the handshake. That is what the probes below do, and it is also why a full enumeration costs one connection per suite and belongs on infrastructure you own.

Rank what you find in this order. Suites with ECDHE provide forward secrecy, so a stolen private key cannot decrypt recorded traffic. Suites ending in GCM, POLY1305 or any TLS 1.3 name are AEAD, which removes the padding oracle class of attack that CBC suites carry. A suite with neither property is the finding worth writing up.

Prerequisites

Steps

  1. Step 1.

    List what your client can offer for TLS 1.3. These three suites are the whole TLS 1.3 set.

    openssl ciphers -v -s -tls1_3
    
    TLS_AES_256_GCM_SHA384         TLSv1.3 Kx=any      Au=any   Enc=AESGCM(256)            Mac=AEAD
    TLS_CHACHA20_POLY1305_SHA256   TLSv1.3 Kx=any      Au=any   Enc=CHACHA20/POLY1305(256) Mac=AEAD
    TLS_AES_128_GCM_SHA256         TLSv1.3 Kx=any      Au=any   Enc=AESGCM(128)            Mac=AEAD

    Kx=any and Au=any are the TLS 1.3 change: key exchange and authentication left the suite name.

  2. Step 2.

    Read the suite the server picks when you offer everything.

    openssl s_client -connect www.cloudflare.com:443 -servername www.cloudflare.com -tls1_2 </dev/null 2>/dev/null | grep 'Cipher    :'
    
        Cipher    : ECDHE-ECDSA-CHACHA20-POLY1305

    Read the suite from this indented Cipher : line inside the session block, not from the New, summary line above it.

  3. Step 3.

    Probe the four families that decide the verdict, one connection each.

    for fam in "ECDHE+AESGCM" "ECDHE+SHA" "kRSA" "aNULL"; do if openssl s_client -connect www.cloudflare.com:443 -servername www.cloudflare.com -tls1_2 -cipher "$fam@SECLEVEL=0" </dev/null >/dev/null 2>&1; then echo "$fam: accepted"; else echo "$fam: refused"; fi; done
    
    ECDHE+AESGCM: accepted
    ECDHE+SHA: accepted
    kRSA: accepted
    aNULL: refused

    kRSA: accepted is the finding. That family has no forward secrecy. The suite negotiated there was AES128-GCM-SHA256.

  4. Step 4.

    Find out who decides the order. Offer two suites, then offer the same two reversed.

    for c in "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305" "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256"; do openssl s_client -connect www.cloudflare.com:443 -servername www.cloudflare.com -tls1_2 -cipher "$c" </dev/null 2>/dev/null | grep 'Cipher    :'; done
    
        Cipher    : ECDHE-ECDSA-AES128-GCM-SHA256
      Cipher    : ECDHE-ECDSA-CHACHA20-POLY1305

    The answer followed the order sent, so this server does not enforce its own preference between these two.

How to read the result

| What you see | What it means | What to do | | --- | --- | --- | | Cipher : ECDHE-...-GCM or TLS_AES_... | Forward secrecy and an AEAD cipher | Pass. Record the name in the release notes. | | Cipher : AES128-GCM-SHA256 with no ECDHE | RSA key exchange, no forward secrecy | Raise it. One leaked private key exposes every recorded session. | | Cipher : ...-SHA or ...-SHA256 on a CBC suite | CBC with HMAC, not AEAD | Acceptable in TLS 1.2, first candidate for removal. | | SSL alert number 40 | The server shares no suite in that family | The family is off. This is the result you want for aNULL. | | no cipher match with no alert number | Your OpenSSL has no such suite | Not a server result. Rerun with a family your build carries. |

Common mistakes

Sign: A probe for RC4 or 3DES returns no cipher match and the family is recorded as disabled.Cause: Modern OpenSSL builds are compiled without RC4 and 3DES, so openssl ciphers -v RC4 fails locally with the same message. Nothing was sent. A server refusal always carries an SSL alert number.
Sign: The New line names a different suite from the Cipher line in the same output.Cause: They print different things. The New line shows the protocol version the cipher was introduced in, so a TLS 1.2 connection using AES128-SHA prints New, SSLv3. The indented Cipher line inside the session block is the negotiated suite.
Sign: The suite list looks clean but an old client still gets a weak suite.Cause: The probe ran with your client's default list, which already excludes the weak families. Add @SECLEVEL=0 so the offer includes them, then read the alert number to see what the server does.

Thresholds

TLS 1.3 defines exactly three suites for general use Source: RFC 8446 section B.4, confirmed by the step 1 output

What to check next

FAQ

How to check cipher suites in Linux?

The same commands. openssl ciphers -v lists the local set and s_client -cipher probes a remote one. Neither needs a distribution specific package beyond openssl itself.

How to check SSL cipher suites with nmap?

nmap --script ssl-enum-ciphers -p 443 host enumerates the full list and grades it. It opens a connection per suite per version, which is heavy against a host you do not own, so keep it for your own infrastructure.

How do I check for weak ciphers specifically?

Probe the families rather than individual names: aNULL, eNULL, EXPORT, kRSA and SHA on CBC suites. Each accepted family is a finding on its own, and the family names survive OpenSSL version changes better than suite names.

Why does the server ignore my preference order sometimes?

A server can be configured to enforce its own order. When it does, both runs in step 4 return the same suite regardless of the order you sent, which is how you tell the two configurations apart.

Verified

Verified by Maks Vernyopenssl 3.1.1

Each output block is what the command above it printed on that date, on the host named in the step. Figures read from a live site move between runs. Compare the shape of the answer rather than the digits, and see the methodology for how a page is re-verified.

intermediate7 minpublished updated Maks Verny