The eMudhra fix lives in your Go binary, not in your Docker image
On October 19, Go shipped two security releases: 1.27.3 and 1.26.10. The fix touches a single package, crypto/x509. I rebuilt one of my Go services on the same Docker image as before. Go's own scanner still found the fla
On October 19, Go shipped two security releases: 1.27.3 and 1.26.10. The fix touches a single package, crypto/x509. I rebuilt one of my Go services on the same Docker image as before. Go's own scanner still found the flaw.
TL;DR: the eMudhra fix is a list of eight banned keys, written into the code of
crypto/x509. That list is compiled into your binary. Updating the base image or the system certificates does not add it. You have to rebuild with Go 1.27.3 or 1.26.10, then redeploy. If your Dockerfile still builds with Go 1.25, rebuilding changes nothing: that version gets no more fixes.
Versions and source status: snapshot as of October 31, 2026. Check the latest fixes before deploying.
This article is for Go developers and for teams that ship Go services in containers.
What happened with eMudhra
eMudhra is a certificate authority. Most browsers and operating systems accept its certificates.
On October 17, Mozilla blocked eight of its intermediate certificates in Firefox. An intermediate certificate is signed by the root. It is the one that then signs website certificates.
The private keys of those eight certificates were very weak. They could be recovered in microseconds. With one of those keys, anyone can sign a TLS certificate for any domain. They can then sit between your service and the API it calls, and read the traffic.
Go shipped its fix two days later. It was the project's first security release on the "URGENT" track. Usually the team announces a security fix three to seven days before it ships. This time it shipped with no advance notice.
My lab: the same service, three binaries
Only the last two rows compare the same code with two compilers. The April binary is a historical reference.
One of my projects has about ten Go services. They all declare go 1.25.0 in their go.mod. Their Dockerfile builds with golang:1.25-bookworm. It then copies the binary into a distroless image.
I ran one of those services through govulncheck. It is Go's official tool for finding known vulnerabilities. With -mode=binary, it analyzes a compiled binary directly.
In Go's database, the eMudhra flaw is GO-2026-6809. It is still there with Go 1.25.14, and for a good reason: that version never got the fix.
I checked Docker Hub. The golang:1.25-bookworm image is the same as golang:1.25.14-bookworm. It has not changed since August 19.
Go's release policy explains why. Each major release is supported until two newer ones exist. Go 1.27 shipped on August 19. So Go 1.25 gets no fixes at all anymore. With my Dockerfile, rebuilding a hundred times fixes nothing.
The 13 vulnerabilities left with Go 1.27.3 come from my dependencies: golang.org/x/net, golang.org/x/text, gRPC, pgx and OpenTelemetry. Rebuilding fixes the standard library. Modules are updated separately, with go get and tests. I covered that part in my article on the Go supply chain.
The fix is a list, compiled into your binary
I opened the Go 1.27.3 source. The fix adds a table to crypto/x509/verify.go:
// distrustedSPKIHashes contains hashes of SPKIs from certificates which
// we don't consider to be trustworthy. We use these hashes to filter out
// chains returned by Verify
var distrustedSPKIHashes = map[string]struct{}{
"ebed6475c2c2d80fcb199f3fe8dd6e6b3ce60b77eb15d4e42c3dec0c6f5b5105": {},
"49dd353dc2b919a3770645117d0e60c16115e7d1bc259eefe734b00ea434fe5f": {},
// ... six more hashes
}
Each line is the SHA-256 hash of a public key. Verify builds the possible certificate chains. It drops every chain that contains one of those keys. If no chain is left, the TLS connection fails.
Here is the key point. Go compiles its standard library into every binary. So this table lives inside your executable, not on the server's disk.
Updating the ca-certificates package in your image does not bring it in. That package holds root certificates. It cannot exclude an intermediate certificate. The Go team says it in its post: Linux has no system mechanism to revoke an intermediate. That is why it shipped an urgent release.
On Linux, Go checks certificates itself. That covers almost all of your containers. A binary built with an older version still accepts those eight keys.
Nobody watched the line that picks the compiler
Those services do have Dependabot. Dependabot is GitHub's bot that proposes dependency updates. In these repositories, it only watches Go modules, and only my shared internal module.
So the FROM golang:1.25-bookworm line is watched by nothing. The frontend repository watches its Docker images. So does the Keycloak one. The Go services do not.
I also found three Go versions in the same project. My CI runner image ships Go 1.25.8 for the tests. The Dockerfile builds with Go 1.25.14 today. The binary left on my laptop came from Go 1.25.0. The tests pass. They say nothing about the version that built the image.
The lesson is simple. The compiler version is a dependency. It needs watching like the others.
Check what runs, not what is written
For comparable scans, save the date, govulncheck version, commit and build flags. The vulnerability database changes. My lab totals are not expected results for your future scans.
date -u +%FT%TZ
govulncheck -version
git rev-parse HEAD
Your go.mod gives a minimum version. It does not say what built your binary. To find out, ask the binary itself:
go version -m ./my-service | head -1
# ./my-service: go1.25.14
A distroless image usually has no shell. An executable it contains can still run directly. For this check, copy the binary out:
docker create --name check registry/my-service:tag
docker cp check:/my-service ./my-service
docker rm check
govulncheck -mode=binary ./my-service
govulncheck exits with code 3 when it finds a vulnerability. A CI step that runs it fails on its own.
One detail surprised me. My Dockerfile builds with -ldflags="-s -w". That option strips the symbol table from the binary. But govulncheck uses it to know which functions are present. Without it, the tool reports more broadly.
On the same code with Go 1.25.14, it reports 52 vulnerabilities instead of 21. The eMudhra flaw still shows up. For a precise result, also run govulncheck ./... on the source code in your CI. Keep binary mode to check the image you ship.
What to change
For Dependabot, a few lines in .github/dependabot.yml are enough:
- package-ecosystem: docker
directory: /
schedule:
interval: weekly
The checklist
A standard library fix does not exist until your binary is rebuilt.
- [ ] Run
go version -mon the binary of every deployed image - [ ] Check that every binary uses at least go1.27.3 or go1.26.10
- [ ] Search all your Dockerfiles for
FROM golang:1.25and older - [ ] Have Dependabot or Renovate watch the
dockerecosystem - [ ] Run
govulncheckin CI, on the source code and on the binary - [ ] Align the CI Go version with the image Go version
- [ ] Rebuild and redeploy the services nobody has touched in a long time
- [ ] Update your modules separately, with
go getand tests
The takeaway
In Go, part of your TLS security lives in your binary. When Go fixes its standard library, your service stays vulnerable until the next build. And if your build image is no longer supported, you never get a build containing the fix.
Open each of your Dockerfiles today and look at the FROM golang line. It is the dependency nobody reviews.
Want a review of your Go images and your build pipeline? See my DevSecOps pipeline offer, or let's talk.
Sources: Go blog, response to the eMudhra incident Β· Go vulnerability database, GO-2026-6809 (CVE-2026-111943) Β· Go, change 851047 Β· Go release history and policy Β· Go security policy Β· govulncheck documentation Β· Docker Hub, golang image
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes β full credit and traffic to the original publisher.