skip to content

What does tls.Config.InsecureSkipVerify = true actually turn off in a Go TLS client?

level: juniorimportance: must knowfreq 58%

answer

  1. it is not only about expiry
  2. one boolean, two separate checks
  3. encrypted to whom, exactly
  4. chain plus name, both gone
  5. the name is still sent, just never compared

basics

~20 s

It turns off both checks a Go TLS client makes on the peer's certificate: the chain is no longer verified against any trust root, and the name in the certificate is no longer matched. Any certificate from anyone is accepted.

solid answer

~50 s

`InsecureSkipVerify` is a single boolean that disables the whole of certificate verification, not one awkward part of it. With it set, `crypto/tls` accepts any certificate the peer presents, from any issuer, carrying any name, so the client no longer knows who it is talking to. The connection is still encrypted, but encrypted to whoever answered — which is exactly what an on-path attacker needs. There is no partial mode: you cannot ask for "verify the chain but skip the hostname". `ServerName` is still sent in the handshake for virtual hosting, but nothing is compared against it any more. When a handshake fails, the right response is to fix the inputs to verification — point `RootCAs` at the CA that signed the peer, or set `ServerName` to the name the certificate actually carries — rather than to switch the check off.

code

go · 3 lines
go
conn, err := tls.Dial("tcp", "collector.internal:9000", &tls.Config{
	InsecureSkipVerify: true, // accepts any certificate, any issuer, any name
})

go deeper

for a junior

Be able to say in one sentence that the flag disables both the chain check and the hostname check, and that encryption without verification tells you nothing about who is on the other end.

for a middle

Explain the two distinct verification steps it removes, which error message each step produces, and what ServerName still does when the flag is set.

for a senior

Show that you treat it as a security regression rather than a config knob: name the correct fix for each failure it masks, and describe how you would prevent it reappearing across services.

for a principal

Own the position that this flag is never an acceptable production default, and be ready to say what infrastructure the org must provide so nobody needs it: a distributed internal CA bundle and certificates whose names match what clients dial.

## The field In Go, every TLS connection is configured by a `*tls.Config`. One of its fields is: ```go InsecureSkipVerify bool ``` Its documented meaning is blunt: it controls whether a client verifies the server's certificate chain **and** host name. If it is true, `crypto/tls` accepts any certificate presented by the server and any host name in that certificate. ## The two checks it removes A TLS client normally does two independent things with the certificate the server sends. 1. **Chain building and verification.** The leaf certificate must chain, through zero or more intermediates, to a certificate the client already trusts. That trust set is `tls.Config.RootCAs`, or the host's system trust store when `RootCAs` is nil. This step also enforces validity dates, basic constraints and key usages. Failure here surfaces as `x509: certificate signed by unknown authority`. 2. **Name checking.** The name the client wanted to reach must appear in the certificate's Subject Alternative Names. That name is `tls.Config.ServerName`, defaulted from the address you dialled. Failure here surfaces as `x509: certificate is valid for a.example, not b.example`. `InsecureSkipVerify` removes both at once. There is no flag that removes only one. ## Why "but it is still encrypted" is not a defence TLS gives you confidentiality, integrity and authentication. The first two are meaningless without the third: encryption protects the channel to the party you completed the handshake with, and verification is the only thing that tells you which party that was. With verification off, anyone who can answer the connection — a compromised load balancer, a DNS or ARP hijack, a transparent proxy, a laptop on the same network — can present a certificate they minted five seconds ago and the client will accept it, decrypt your credentials, and forward the traffic on so nothing looks broken. That is the textbook machine-in-the-middle, and the flag is what enables it. "It is only inside the VPC" does not save it either: the whole point of transport security on an internal network is that the network is not the security boundary. ## What ServerName does when the flag is set This catches people out. `ServerName` has two jobs: it is the name verified against the certificate, and it is the SNI value sent in the ClientHello so a server hosting several names can pick the right certificate. Setting `InsecureSkipVerify` only cancels the first job. The name is still sent (unless it is an IP address), so a handshake against a virtual-hosted server still works — which is part of why the flag can appear to "fix" things without anyone noticing what was lost. ## The two failures it hides, and their real fixes Almost every commit that adds this flag is fixing one of two errors, and each has a narrow, correct fix. - *Unknown authority.* The peer is signed by an internal CA, or the client's environment has no trust store at all (a `scratch` container image with no CA bundle). Fix: give the client the right roots — install the CA bundle in the image, or set `RootCAs` to a pool containing the internal root. - *Name mismatch.* You dialled an IP or a load-balancer address, and the certificate names the service. Fix: set `ServerName` to the name the certificate carries, while continuing to dial the address you need. Both fixes keep verification on. `InsecureSkipVerify` "fixes" both by declining to look. ## Where it is defensible Very few places. A test that spins up a throwaway server with a self-signed certificate is the usual one, and even there the better move is to hand the test's own CA to `RootCAs` so the test exercises the same code path production uses. If you genuinely must accept an unverifiable certificate and still pin an identity, the shape is `InsecureSkipVerify: true` **plus** a `VerifyPeerCertificate` callback that does the verification itself — the flag alone is never the answer. ## The practical risk The field is a one-line change that turns a red build green, which is why it spreads: it gets committed as a debugging workaround, then copy-pasted into every other service that talks to the same endpoint. Treat it as a change that needs the same scrutiny as deleting an authentication check, because that is what it is.

  • If the client accepts any certificate, is the traffic still encrypted?
    Yes, and that is the trap. The handshake still negotiates keys and the bytes on the wire are still ciphertext, but they are encrypted to whichever party completed the handshake. Without verification you have no evidence that party is the service you meant to reach, so an on-path attacker who terminates the connection and re-dials the real server reads and rewrites everything while the client sees a perfectly healthy TLS session.
  • Can you skip only the hostname check and keep chain verification?
    Not with that flag — it is all or nothing. If the certificate is trustworthy but carries a different name than the address you dial, set `ServerName` to the name the certificate actually holds and keep verification on. If you need a genuinely custom rule, keep normal verification enabled and add it in a `VerifyPeerCertificate` or `VerifyConnection` callback, which run in addition to the standard checks rather than instead of them.
  • A teammate says it is fine because the call never leaves the private network. How do you answer?
    Internal networks are not an authentication boundary: anything that can answer the address — a misrouted service, a stale DNS record, a compromised sidecar, an engineer's laptop on the same subnet — becomes a valid peer. The certificate is what distinguishes the intended service from anything else that can accept a TCP connection, and the private CA that signs internal certificates exists precisely so internal clients can still verify.

It is like checking that a courier's ID looks laminated but never reading the name or the issuer: you still get a sealed envelope, you just have no idea who handed it to you.

saying these in an interview costs you the question

  • Says it only skips the certificate expiry check
  • Argues the connection is still encrypted so it is safe
  • Treats it as acceptable because the traffic stays inside a private network
  • Believes it is test-only, then ships it in production code
  • Thinks it also stops the client sending SNI
  • Calls it the fix for an unknown-authority error