Keyboard shortcuts

Press ← or → to navigate between chapters

Press ? to show this help

Press Esc to hide this help

Nuances: TLS to SQL Server

What could surprise you here, why it is so, and what to do. Each entry has a level, 1 (day one) to 5 (expert): the index lists them all by level.

SQL Server connections are verified, and trust_server_certificate is for labs (level 3)

  • What: every SQL Server connection is TLS for the whole session, and the engine checks the server's certificate against this server's trusted certificates plus ca: (a certificates: entry), for the name in host. trust_server_certificate: true accepts any certificate: anyone who can answer at the server's address (a DNS entry, a spoofed address) gets the login and every query. The start log and ductus validate warn about it for a server on another machine, doctor says "not verified (trust_server_certificate)" with what verifying would make of the certificate, and the Connections page shows the lock open. It's refused next to ca: or name_in_certificate: (they'd never be checked). There's no encrypt: false: SQL Server would then encrypt only the login and send queries and results readable.
  • Why: many SQL Servers present the certificate SQL Server generates for itself when none is configured, which nobody can verify: it's self-signed, an old X.509 v1 certificate (refused as such: "an old X.509 v1 certificate, which can't be verified"), and its name is always SSL_Self_Signed_Fallback, whatever the server is called (seen on 2022 and 2025). So drivers' "trust the server certificate" switch became a habit.
  • What to do: ask the DBA for a certificate from your CA for the name you connect by (SQL Server Configuration Manager on Windows; mssql-conf network.tlscert and network.tlskey on Linux), name the CA in ca: unless this server already trusts it, and drop trust_server_certificate. Refusals say what's wrong: "not trusted: its issuer isn't one this server trusts", "the certificate isn't for 10.1.2.3", "has expired".

Connecting by a name the certificate doesn't list: name_in_certificate (level 3)

  • What: the certificate must be for the name in host. An availability group listener, a DNS alias or an IP address often isn't on it. name_in_certificate: sql1.hospital.org (ADO.NET's HostNameInCertificate) checks the certificate for that name instead, while connecting to host.
  • What to do: use it rather than trust_server_certificate: the certificate is still checked, only for another of the server's names. Better still, ask for the listener's name on the certificate.

encrypt: strict is TDS 8: TLS before anything (level 3)

  • What: by default (mandatory), as every SQL Server client, the engine first exchanges PRELOGIN packets in the clear, then makes the TLS handshake inside them; this always negotiates TLS 1.2 (TDS 7 can't carry TLS 1.3). encrypt: strict makes the handshake first, asking for tds/8.0, with TLS 1.3 when the server allows it. Servers set to force strict accept nothing else. SQL Server accepts strict only with a certificate configured for its own host name, never with its generated one, and SQL Server 2022 on Linux refuses strict whatever it's given (tested with CU27); SQL Server 2025 on Linux accepts it. ADO.NET ignores TrustServerCertificate with strict and always verifies; here trust_server_certificate still turns verification off (for labs), warned about as without strict.
  • What to do: leave encrypt out unless the DBA forces strict; then set encrypt: strict. A server that doesn't accept strict closes the connection during the handshake, and the error says to leave encrypt: strict out.

SQL Server 2022 on Linux doesn't accept encrypt: strict (level 3)

  • What: tested against SQL Server 2022 on Linux (CU27, Microsoft's container image): it refuses every TDS 8 strict connection with "A valid TLS certificate is not configured to accept strict (TDS 8.0 and above) connections", whatever it's given: its generated certificate, a CA's certificate for its own host name (CN and subjectAltName, server authentication, key encipherment), TLS 1.2 and 1.3 or 1.3 alone. SQL Server 2025 on Linux (CU10) accepts strict with the same certificate. SQL Server 2022 on Windows isn't tested here (Microsoft documents strict for it). The default (mandatory: TLS after PRELOGIN, verified) works with both.
  • Why: not documented: Microsoft's page on encrypted connections for SQL Server on Linux doesn't mention strict for any version. Measured, not explained.
  • What to do: against SQL Server 2022 on Linux, leave encrypt out: the connection is still TLS throughout and verified. Use encrypt: strict with 2025, or on Windows once tried there. The engine's own tests run against 2025 for this reason.

SQL Server's certificate is read through PRELOGIN, and watched when verified (level 3)

  • What: doctor and the engine's own check read a SQL Server's certificate as the engine's connection meets it: a PRELOGIN packet asking for encryption (its answer gives the server's version), then the handshake inside PRELOGIN packets (with encrypt: strict, plain TLS). Nothing logs in, in the scheduled check. A verified connection's certificate is kept and alerted at 30, 14 and 7 days under the entry's name; one with trust_server_certificate is described, not watched (an expired certificate doesn't stop a connection that checks nothing). A named instance without port: is found through the SQL Server Browser first, and its certificate is kept under the port it answered with. The check takes ~35 ms through PRELOGIN (TLS 1.2), ~0.55 s with strict (TLS 1.3: see "Doctor makes the engine's handshake").
  • What to do: verify the connection, and the certificate is watched. A SQL Server reads its certificate at start: after the DBA renews it, the server's restart is what makes it count (and "Check now" shows it).

Verification costs nothing measurable (level 4)

  • What: opening a SQL Server connection (PRELOGIN, TLS, login, a first query) took ~120–140 ms in every mode against a local SQL Server 2025: trust_server_certificate, verified, and strict verified (scripts/bench-mssqltls.sh; differences within the noise between rounds). Pools keep their connections (closed after 5 idle minutes), so messages rarely wait for one.
  • What to do: verify; there's no speed to gain by not.