How do you restrict TLS protocol versions and cipher suites on the embedded server?
answer
- protocol = SSLContext base (default TLS)
- enabled-protocols = version allowlist (drop 1.0/1.1)
- ciphers = suite allowlist, mostly affects 1.2
- TLS 1.3 has fixed suites, ignores legacy list
- empty intersection = handshake failure
basics
~10 sUse server.ssl.enabled-protocols to whitelist versions (e.g. TLSv1.3, TLSv1.2) and server.ssl.ciphers to whitelist cipher suites. server.ssl.protocol sets the base SSLContext protocol (default TLS).
solid answer
~40 sThree properties control this. server.ssl.protocol names the SSLContext instance the JVM creates — default 'TLS', which lets the JVM pick the highest mutually supported version. server.ssl.enabled-protocols is the list you actually allow, e.g. TLSv1.3 and TLSv1.2, so you can drop deprecated TLSv1.0/1.1. server.ssl.ciphers whitelists specific cipher suites (like TLS_AES_256_GCM_SHA384 for 1.3 or TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 for 1.2); anything not listed is refused. In practice you set enabled-protocols to lock the version floor and, if compliance requires, ciphers to enforce strong AEAD suites. Note TLS 1.3 ignores the classic cipher list and uses its own fixed suite set, so restricting ciphers mainly affects 1.2. These map onto the embedded connector's SSL host config regardless of Tomcat/Jetty/Undertow/Netty.
code
yaml · 9 linesserver:
ssl:
key-store: "file:/etc/katajob/keystore.p12"
key-store-password: "${KEYSTORE_PASSWORD}"
protocol: TLS # SSLContext base; rarely changed
enabled-protocols: TLSv1.3,TLSv1.2 # drop deprecated 1.0/1.1
ciphers: # allowlist; mainly constrains TLS 1.2
- TLS_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384go deeper
Know that enabled-protocols and ciphers exist to harden TLS.
Distinguish protocol vs enabled-protocols and know 1.0/1.1 should be dropped.
Explain the TLS 1.3 fixed-suite subtlety and JSSE vs OpenSSL naming.
Balance compliance allowlists against client compatibility and know where cipher ordering lives (customizer, not a plain property).
## The three knobs - **`server.ssl.protocol`** — the *name passed to `SSLContext.getInstance(...)`*. Default is `"TLS"`, which means 'the best TLS version both sides support'. You almost never change this; setting it to a specific version (e.g. `TLSv1.2`) pins the context but is coarser than `enabled-protocols`. - **`server.ssl.enabled-protocols`** — the concrete *list of protocol versions the connector will accept*, e.g. `TLSv1.3,TLSv1.2`. This is how you disable legacy `TLSv1.0`/`TLSv1.1`, which are deprecated and fail most security scans. If unset, the JVM/connector defaults apply (modern JDKs already disable 1.0/1.1, but being explicit is defensible in audits). - **`server.ssl.ciphers`** — the *whitelist of cipher suites*. A cipher suite bundles the key exchange, authentication, bulk encryption, and MAC (e.g. `TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256`). Only listed suites are offered/accepted; the server picks from the intersection with the client. ## TLS 1.3 subtlety TLS 1.3 redefined cipher suites — it has a small fixed set (`TLS_AES_128_GCM_SHA256`, `TLS_AES_256_GCM_SHA384`, `TLS_CHACHA20_POLY1305_SHA256`) that is **not** controlled by the legacy `ciphers` list on most stacks. So `server.ssl.ciphers` primarily constrains TLS 1.2. If you must forbid weak 1.2 suites, list only AEAD/forward-secret ones (ECDHE + GCM). To force 1.3-only, set `enabled-protocols: TLSv1.3`. ## YAML example ```yaml server: ssl: enabled-protocols: TLSv1.3,TLSv1.2 ciphers: - TLS_AES_256_GCM_SHA384 - TLS_AES_128_GCM_SHA256 - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ``` ## Where the values also live When using **SSL bundles** (`spring.ssl.bundle.*`), the same options move under `...options.enabled-protocols` and `...options.ciphers` on the bundle, and the connector references the bundle via `server.ssl.bundle`. The semantics are identical; only the property location changes. ## Gotchas - **Empty intersection = handshake failure.** If you restrict to suites/protocols a client can't do, connections drop with `no cipher suites in common` / `protocol_version` alerts. Test against your real clients. - **Cipher names differ by provider.** JSSE names (`TLS_ECDHE_...`) vs OpenSSL names (`ECDHE-RSA-AES128-GCM-SHA256`) — with the JSSE/Java stack use the JSSE form. - **Ordering / server preference** is a container concern (e.g. Tomcat's `useServerCipherSuitesOrder`), not exposed as a plain `server.ssl.*` property; use a `WebServerFactoryCustomizer` if you need it. - Don't confuse `protocol` (the SSLContext base) with `enabled-protocols` (the accepted version list) — the latter is what hardens you. ## When to use Always set `enabled-protocols` for internet-facing embedded TLS to satisfy scanners/compliance (PCI, FIPS-adjacent). Set `ciphers` when policy demands a specific strong-suite allowlist; otherwise modern JDK defaults are already sane.
- Why does setting server.ssl.ciphers seem to have no effect when the handshake uses TLS 1.3?TLS 1.3 uses its own fixed, small set of cipher suites that the legacy ciphers property doesn't control on most stacks. To influence what 1.3 offers you generally can't via that list; to force 1.3-only or restrict 1.2 suites you use enabled-protocols plus the ciphers list respectively.
- What is the difference between server.ssl.protocol and server.ssl.enabled-protocols?protocol is the string passed to SSLContext.getInstance (default 'TLS', meaning negotiate the best version). enabled-protocols is the concrete list of versions the connector will actually accept, which is how you disable TLSv1.0/1.1.
saying these in an interview costs you the question
- Claiming server.ssl.ciphers fully controls TLS 1.3 suites.
- Thinking server.ssl.protocol=TLSv1.2 is the way to disable 1.0/1.1 (use enabled-protocols).
- Listing OpenSSL-style cipher names with the JSSE stack.
- Restricting suites without testing real clients, causing handshake failures.